/* * TiffImage.cpp * TXS * * Created by Steven An * Copyright (c) 2004 __MyCompanyName__. All rights reserved. * */ #include "TiffImage.h" #include #include #include "txs_globals.h" using namespace TXS; ostream& operator<<(ostream &s, NormRGB &p) { return s << "NormRGB(" << p.r << ", " << p.g << ", " << p.b << ")"; } ostream& operator<<(ostream &s, UVs &x) { return s << "UVs(" << x.u << ", " << x.v << ")"; } // read in a BMP into the buffer TiffImage::TiffImage( char *filename ) { this->rgb = NULL; this->readFromTiff(filename); } void TiffImage::readFromTiff(char *filename) { __LOG__("opening " << filename); // copied from the LibTiff docs TIFF* tif = TIFFOpen(filename, "r"); if (tif) { __LOG__("opened " << filename); uint32 w, h; size_t npixels; uint32* raster; TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h); npixels = w * h; raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32)); if (raster != NULL) { if (TIFFReadRGBAImage(tif, w, h, raster, 0)) { //-------------------------------- // convert 32-bit pixels into 4 8-bits //-------------------------------- width = w; height = h; if(rgb != NULL) delete rgb; rgb = new NormRGB[ npixels ]; __LOG__("preparing to read " << npixels << "pixels.."); for( uint32 i = 0; i < npixels; i++ ) { // treat the 32-bit int like an array of 4 bytes (uchar's) unsigned char* rgba = (unsigned char*)(&raster[i]); // now normalize and store the components rgb[i].r = (TXS_REAL)rgba[0] / 255.0; rgb[i].g = (TXS_REAL)rgba[1] / 255.0; rgb[i].b = (TXS_REAL)rgba[2] / 255.0; } } _TIFFfree(raster); } TIFFClose(tif); __LOG__("read in " << filename << " - " << width << "x" << height); __LOG__("first pixel is " << this->rgb[0]); } else { __ERROR__("TiffImage:ERROR:Could not read in TIFF: \"" << filename << "\""); } } /* * Create a blank slate */ TiffImage::TiffImage(int width, int height) { this->width = width; this->height = height; this->rgb = new NormRGB[this->width * this->height]; // TEMP this->fill(NormRGB(1.0, 1.0, 0.0)); //this->drawDiagonalCross(NormRGB(1.0, 1.0, 1.0)); } TiffImage::~TiffImage() { // BAM!! BUH-LEETED!!! delete this->rgb; } int inclusive_bound(int x, int a, int b) { if(x <= a) return a; else if(x >= b) return b; else return x; } NormRGB TiffImage::getRgbAtUVNoWrap(UVs uv) { const TXS_REAL u = uv.u; const TXS_REAL v = uv.v; int x = inclusive_bound((int)floor( u * width ), 0, width - 1); int y = inclusive_bound((int)floor( v * height ), 0, height - 1); return rgb[ y*width + x ]; } NormRGB TiffImage::getRgbAtUV(UVs uv) { const TXS_REAL u = uv.u; const TXS_REAL v = uv.v; int xmod = (int)floor( u * width ) % width, ymod = (int)floor( v * height ) % height; // correct for negative remainders (we want modulo, not remainder) if( xmod < 0 ) xmod += width; if( ymod < 0 ) ymod += height; //__DBa("x " << xmod << " y " << ymod << " w " << width << " h " << height); return rgb[ ymod*width + xmod ]; } /* * Converts an continuos RGB value in [0,1] into * discrete value in [0, 255] */ char normToCharRGB(TXS_REAL x) { return (unsigned char)floor(x * 255); } bool TiffImage::writeToTiff(char *filename) { TIFF* tif = TIFFOpen(filename, "w"); if (tif != NULL) { const int BPS = 8; const int SPP = 3; uint32 w = this->width; uint32 h = this->height; uint32 npixels = w *h; TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h); TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, BPS); // u can't use sizeof(char) here for some reason.. TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, SPP); char* raster = NULL; raster = (char*) malloc(npixels * sizeof (char) * SPP); if (raster != NULL) { // Remember, 0,0 is the BOTTOM left corner of the image. // So we need to do some index-fudging to write it correctly // Convert the image from NormRGB to [0,255] here for(int x = 0; x < w; x++) for(int y = 0; y < h; y++) { int pixIdx = ((h-y-1) * w + x) * 3; int myPixIdx = y*w + x; raster[pixIdx + 0] = normToCharRGB(this->rgb[myPixIdx].r); raster[pixIdx + 1] = normToCharRGB(this->rgb[myPixIdx].g); raster[pixIdx + 2] = normToCharRGB(this->rgb[myPixIdx].b); } if (TIFFWriteEncodedStrip(tif, 0, raster, npixels * SPP) == 0) { __ERROR__("Could not write strip"); } // Make sure to use free, and not TIFF free. cuz i used malloc, not TIFF malloc // using Tifffree with malloc causes random crashing // TIFF malloc doesn't work for some reason // and in the libtiff manual, they use malloc free(raster); } else { __ERROR__("couldn't allocate " << (npixels * 3) << " pixels.."); } TIFFClose(tif); // TODO BUG - we shouldn't return true if raster was not alloc'ed successfully return true; } else { __ERROR__("TiffImage:ERROR:Could not write in TIFF: \"" << filename << "\""); return false; } } TXS_REAL sumSqrDiffs(NormRGB &a, NormRGB &b) { // TEMP DEBUG // just use the R componenet const TXS_REAL dr = a.r - b.r; const TXS_REAL dg = a.g - b.g; const TXS_REAL db = a.b - b.b; return dr*dr + dg*dg + db*db; //return dr*dr; } /* * Computes total SSD between that and this's region (of same size) starting at x0, y0 */ TXS_REAL TiffImage::calcRegionSSD(TiffImage &that, int x0, int y0, int midOfs) { int tw = that.getWidth(); int th = that.getHeight(); TXS_REAL SSD = 0.0; for(int x = 0; x < tw; x++) { for(int y = 0; y < th; y++) { // ignore the middle pixel // unless midOfs is -1 if(midOfs != -1) { // some middle offset was given // we may want to skip this pixel if(x == midOfs && y == midOfs) // skip it! continue; } NormRGB &myRgb = this->getRGB(x0 + x, y0 + y); NormRGB &otherRgb = that.getRGB(x, y); SSD += sumSqrDiffs(myRgb, otherRgb); } } return SSD; } void TiffImage::getBestMatchingRegion(TiffImage &img, int &xOut, int &yOut, int midOfs) { // calculate limits const int w = this->getWidth(); const int h = this->getHeight(); const int lastX = w - img.getWidth(); const int lastY = h - img.getHeight(); //__DBa("lastX " << lastX << " w " << w); assert(lastX > 0 && lastY > 0); // now find the min SSD TXS_REAL minSSD = 0.0; bool firstIter = true; // note the <= in these loops. this is because lastX/Y are LAST indexs, not boundaries for(int x = 0; x <= lastX; x++) { for(int y = 0; y <= lastY; y++) { TXS_REAL currSSD = this->calcRegionSSD(img, x, y, midOfs); if(firstIter || currSSD < minSSD) { minSSD = currSSD; xOut = x; yOut = y; firstIter = false; } } } }