Feature TinyImageFeatureExtractor::operator()(const CByteImage& img_) const { CFloatImage tinyImg(_targetW, _targetH, 1); /******** BEGIN TODO ********/ // Compute tiny image feature, output should be _targetW by _targetH a grayscale image // Steps are: // 1) Convert image to grayscale (see convertRGB2GrayImage in Utils.h) // 2) Resize image to be _targetW by _targetH // // Useful functions: // convertRGB2GrayImage, TypeConvert, WarpGlobal CByteImage gray; CFloatImage grayF; convertRGB2GrayImage(img_, gray); TypeConvert(gray, grayF); CTransform3x3 scale = CTransform3x3::Scale(1.* img_.Shape().width / _targetW, 1.* img_.Shape().height/ _targetH); WarpGlobal(grayF, tinyImg, scale, eWarpInterpLinear); /******** END TODO ********/ return tinyImg; }
cv::Mat flowToColor(const cv::Mat & flow, float max) { assert(flow.channels() == 2); int rows = flow.rows; int cols = flow.cols; CFloatImage cFlow(cols, rows, 2); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { cFlow.Pixel(j, i, 0) = flow.at<cv::Vec2f>(i, j)[0]; cFlow.Pixel(j, i, 1) = flow.at<cv::Vec2f>(i, j)[1]; } } CByteImage cImage; MotionToColor(cFlow, cImage, max); assert(cImage.Shape().height == rows); assert(cImage.Shape().width == cols); assert(cImage.Shape().nBands == 3); cv::Mat image(rows, cols, CV_8UC3, cv::Scalar(0, 0, 0)); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { image.at<cv::Vec3b>(i, j)[0] = cImage.Pixel(j, i, 0); image.at<cv::Vec3b>(i, j)[1] = cImage.Pixel(j, i, 1); image.at<cv::Vec3b>(i, j)[2] = cImage.Pixel(j, i, 2); } } return image; }
void convertToFloatImage(CByteImage &byteImage, CFloatImage &floatImage) { CShape sh = byteImage.Shape(); //printf("%d\n", floatImage.Shape().nBands); //printf("%d\n", byteImage.Shape().nBands); assert(floatImage.Shape().nBands == min(byteImage.Shape().nBands, 3)); for (int y=0; y<sh.height; y++) { for (int x=0; x<sh.width; x++) { for (int c=0; c<min(3,sh.nBands); c++) { float value = byteImage.Pixel(x,y,c) / 255.0f; if (value < floatImage.MinVal()) { value = floatImage.MinVal(); } else if (value > floatImage.MaxVal()) { value = floatImage.MaxVal(); } // We have to flip the image and reverse the color // channels to get it to come out right. How silly! floatImage.Pixel(x,sh.height-y-1,min(3,sh.nBands)-c-1) = value; } } } }
// Flip the y-axis of an image void FlipImage(CByteImage &imageIn, CByteImage &imageOut) { CShape sh = imageIn.Shape(); assert(imageIn.Shape().nBands == imageIn.Shape().nBands); for (int y=0; y<sh.height; y++) { for (int x=0; x<sh.width; x++) { for (int c=0; c<sh.nBands; c++) { imageOut.Pixel(x,sh.height-y-1,c) = imageIn.Pixel(x,y,c); } } } }
void setDisparities(CByteImage disp, MRF *mrf) { CShape sh = disp.Shape(); int width = sh.width, height = sh.height; int n = 0; for (int y = 0; y < height; y++) { uchar *row = &disp.Pixel(0, y, 0); for (int x = 0; x < width; x++) { mrf->setLabel(n++, row[x]); } } }
void computeCues(CByteImage im1, MRF::CostVal *&hCue, MRF::CostVal *&vCue, int gradThresh, int gradPenalty) { CShape sh = im1.Shape(); int width = sh.width, height = sh.height, nB = sh.nBands; hCue = new MRF::CostVal[width * height]; vCue = new MRF::CostVal[width * height]; int nColors = __min(3, nB); // below we compute sum of squared colordiffs, so need to adjust threshold accordingly (results in RMS) gradThresh *= nColors * gradThresh; //sh.nBands=1; //CByteImage hc(sh), vc(sh); int n = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { uchar *pix = &im1.Pixel(x, y, 0); uchar *pix1x = &im1.Pixel(x + (x < width-1), y, 0); uchar *pix1y = &im1.Pixel(x, y + (y < height-1), 0); int sx = 0, sy = 0; for (int b = 0; b < nColors; b++) { int dx = pix[b] - pix1x[b]; int dy = pix[b] - pix1y[b]; sx += dx * dx; sy += dy * dy; } hCue[n] = (sx < gradThresh ? gradPenalty : 1); vCue[n] = (sy < gradThresh ? gradPenalty : 1); //hc.Pixel(x, y, 0) = 100*hCue[n]; //vc.Pixel(x, y, 0) = 100*vCue[n]; n++; } } //WriteImageVerb(hc, "hcue.png", true); //WriteImageVerb(vc, "vcue.png", true); //exit(1); }
// Convert CFloatImage to CByteImage. void convertToByteImage(CFloatImage &floatImage, CByteImage &byteImage) { CShape sh = floatImage.Shape(); assert(floatImage.Shape().nBands == byteImage.Shape().nBands); for (int y=0; y<sh.height; y++) { for (int x=0; x<sh.width; x++) { for (int c=0; c<sh.nBands; c++) { float value = floor(255*floatImage.Pixel(x,y,c) + 0.5f); if (value < byteImage.MinVal()) { value = byteImage.MinVal(); } else if (value > byteImage.MaxVal()) { value = byteImage.MaxVal(); } // We have to flip the image and reverse the color // channels to get it to come out right. How silly! byteImage.Pixel(x,sh.height-y-1,sh.nBands-c-1) = (uchar) value; } } } }
bool LoadImageFile(const char *filename, CByteImage &image) { // Load the query image. printf("%s\n", filename); Fl_Shared_Image *fl_image = Fl_Shared_Image::get(filename); if (fl_image == NULL) { printf("TGA\n"); ReadFile(image, filename); return true; } else { printf("Not TGA\n"); CShape sh(fl_image->w(), fl_image->h(), 4); image = CByteImage(sh); // Convert the image to the CImage format. if (!convertImage(fl_image, image)) { printf("couldn't convert image to RGB format\n"); return false; } /* Set the alpha channel to 1 everywhere */ int w = sh.width; int h = sh.height; sh = image.Shape(); // printf("shape: %d, %d, %d\n", sh.width, sh.height, sh.nBands); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { image.Pixel(x, y, 3) = 255; } } return true; } }
/******************* TO DO 4 ********************* * AccumulateBlend: * INPUT: * img: a new image to be added to acc * acc: portion of the accumulated image where img is to be added * M: translation matrix for calculating a bounding box * blendWidth: width of the blending function (horizontal hat function; * try other blending functions for extra credit) * OUTPUT: * add a weighted copy of img to the subimage specified in acc * the first 3 band of acc records the weighted sum of pixel colors * the fourth band of acc records the sum of weight */ static void AccumulateBlend(CByteImage& img, CFloatImage& acc, CTransform3x3 M, float blendWidth) { /* Compute the bounding box of the image of the image */ int bb_min_x, bb_min_y, bb_max_x, bb_max_y; ImageBoundingBox(img, M, bb_min_x, bb_min_y, bb_max_x, bb_max_y); int imgWidth = img.Shape().width; int imgHeight = img.Shape().height; CTransform3x3 Minv = M.Inverse(); for (int y = bb_min_y; y <= bb_max_y; y++) { for (int x = bb_min_x; x < bb_max_x; x++) { /* Check bounds in destination */ if (x < 0 || x >= acc.Shape().width || y < 0 || y >= acc.Shape().height) continue; /* Compute source pixel and check bounds in source */ CVector3 p_dest, p_src; p_dest[0] = x; p_dest[1] = y; p_dest[2] = 1.0; p_src = Minv * p_dest; float x_src = (float) (p_src[0] / p_src[2]); float y_src = (float) (p_src[1] / p_src[2]); if (x_src < 0.0 || x_src >= img.Shape().width - 1 || y_src < 0.0 || y_src >= img.Shape().height - 1) continue; int xf = (int) floor(x_src); int yf = (int) floor(y_src); int xc = xf + 1; int yc = yf + 1; /* Skip black pixels */ if (img.Pixel(xf, yf, 0) == 0x0 && img.Pixel(xf, yf, 1) == 0x0 && img.Pixel(xf, yf, 2) == 0x0) continue; if (img.Pixel(xc, yf, 0) == 0x0 && img.Pixel(xc, yf, 1) == 0x0 && img.Pixel(xc, yf, 2) == 0x0) continue; if (img.Pixel(xf, yc, 0) == 0x0 && img.Pixel(xf, yc, 1) == 0x0 && img.Pixel(xf, yc, 2) == 0x0) continue; if (img.Pixel(xc, yc, 0) == 0x0 && img.Pixel(xc, yc, 1) == 0x0 && img.Pixel(xc, yc, 2) == 0x0) continue; double weight = 1.0; // *** BEGIN TODO *** // set weight properly //(see mosaics lecture slide on "feathering") P455 on notebook //Q:How to find the invalid distance ? -> double distance = ((imgWidth - x - 1)*(imgWidth - x - 1) + (imgHeight - y - 1)*(imgHeight - y - 1)); if (distance < blendWidth) { weight = distance / blendWidth; } // *** END TODO *** acc.Pixel(x, y, 0) += (float) (weight * img.PixelLerp(x_src, y_src, 0)); acc.Pixel(x, y, 1) += (float) (weight * img.PixelLerp(x_src, y_src, 1)); acc.Pixel(x, y, 2) += (float) (weight * img.PixelLerp(x_src, y_src, 2)); acc.Pixel(x, y, 3) += (float) weight; } } }
void evaldisp(CFloatImage disp, CFloatImage gtdisp, CByteImage mask, float badthresh, int maxdisp, int rounddisp) { CShape sh = gtdisp.Shape(); CShape sh2 = disp.Shape(); CShape msh = mask.Shape(); int width = sh.width, height = sh.height; int width2 = sh2.width, height2 = sh2.height; int scale = width / width2; if ((!(scale == 1 || scale == 2 || scale == 4)) || (scale * width2 != width) || (scale * height2 != height)) { printf(" disp size = %4d x %4d\n", width2, height2); printf("GT disp size = %4d x %4d\n", width, height); throw CError("GT disp size must be exactly 1, 2, or 4 * disp size"); } int usemask = (msh.width > 0 && msh.height > 0); if (usemask && (msh != sh)) throw CError("mask image must have same size as GT\n"); int n = 0; int bad = 0; int invalid = 0; float serr = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { float gt = gtdisp.Pixel(x, y, 0); if (gt == INFINITY) // unknown continue; float d = scale * disp.Pixel(x / scale, y / scale, 0); float maxd = scale * maxdisp; // max disp range int valid = (d != INFINITY && d <= maxd * 1000); if (valid) { d = __max(0, __min(maxd, d)); // clip disps to max disp range } if (valid && rounddisp) d = round(d); float err = fabs(d - gt); if (usemask && mask.Pixel(x, y, 0) != 255) { // don't evaluate pixel } else { n++; if (valid) { serr += err; if (err > badthresh) { bad++; } } else {// invalid (i.e. hole in sparse disp map) invalid++; } } } } float badpercent = 100.0*bad/n; float invalidpercent = 100.0*invalid/n; float totalbadpercent = 100.0*(bad+invalid)/n; float avgErr = serr / (n - invalid); // CHANGED 10/14/2014 -- was: serr / n //printf("mask bad%.1f invalid totbad avgErr\n", badthresh); printf("%4.1f %6.2f %6.2f %6.2f %6.2f\n", 100.0*n/(width * height), badpercent, invalidpercent, totalbadpercent, avgErr); }
void computeDSI(CByteImage im1, // source (reference) image CByteImage im2, // destination (match) image MRF::CostVal *&dsi, // computed cost volume int nD, // number of disparities int birchfield, // use Birchfield/Tomasi costs int squaredDiffs, // use squared differences int truncDiffs) // truncated differences { CShape sh = im1.Shape(); int width = sh.width, height = sh.height, nB = sh.nBands; dsi = new MRF::CostVal[width * height * nD]; int nColors = __min(3, nB); // worst value for sumdiff below int worst_match = nColors * (squaredDiffs ? 255 * 255 : 255); // truncation threshold - NOTE: if squared, don't multiply by nColors (Eucl. dist.) int maxsumdiff = squaredDiffs ? truncDiffs * truncDiffs : nColors * abs(truncDiffs); // value for out-of-bounds matches int badcost = __min(worst_match, maxsumdiff); int dsiIndex = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { uchar *pix1 = &im1.Pixel(x, y, 0); for (int d = 0; d < nD; d++) { int x2 = x-d; int dsiValue; if (x2 >= 0 && d < nD) { // in bounds uchar *pix2 = &im2.Pixel(x2, y, 0); int sumdiff = 0; for (int b = 0; b < nColors; b++) { int diff = 0; if (birchfield) { // Birchfield/Tomasi cost int im1c = pix1[b]; int im1l = x == 0? im1c : (im1c + pix1[b - nB]) / 2; int im1r = x == width-1? im1c : (im1c + pix1[b + nB]) / 2; int im2c = pix2[b]; int im2l = x2 == 0? im2c : (im2c + pix2[b - nB]) / 2; int im2r = x2 == width-1? im2c : (im2c + pix2[b + nB]) / 2; int min1 = __min(im1c, __min(im1l, im1r)); int max1 = __max(im1c, __max(im1l, im1r)); int min2 = __min(im2c, __min(im2l, im2r)); int max2 = __max(im2c, __max(im2l, im2r)); int di1 = __max(0, __max(im1c - max2, min2 - im1c)); int di2 = __max(0, __max(im2c - max1, min1 - im2c)); diff = __min(di1, di2); } else { // simple absolute difference int di = pix1[b] - pix2[b]; diff = abs(di); } // square diffs if requested (Birchfield too...) sumdiff += (squaredDiffs ? diff * diff : diff); } // truncate diffs dsiValue = __min(sumdiff, maxsumdiff); } else { // out of bounds: use maximum truncated cost dsiValue = badcost; } //int x0=-140, y0=-150; //if (x==x0 && y==y0) // printf("dsi(%d,%d,%2d)=%3d\n", x, y, d, dsiValue); // The cost of pixel p and label l is stored at dsi[p*nLabels+l] dsi[dsiIndex++] = dsiValue; } } } //exit(1); }
/******************* TO DO ********************* * AccumulateBlend: * INPUT: * img: a new image to be added to acc * acc: portion of the accumulated image where img is to be added * M: the transformation mapping the input image 'img' into the output panorama 'acc' * blendWidth: width of the blending function (horizontal hat function; * try other blending functions for extra credit) * OUTPUT: * add a weighted copy of img to the subimage specified in acc * the first 3 band of acc records the weighted sum of pixel colors * the fourth band of acc records the sum of weight */ static void AccumulateBlend(CByteImage& img, CFloatImage& acc, CTransform3x3 M, float blendWidth) { // BEGIN TODO // Fill in this routine // get shape of acc and img CShape sh = img.Shape(); int width = sh.width; int height = sh.height; CShape shacc = acc.Shape(); int widthacc = shacc.width; int heightacc = shacc.height; // get the bounding box of img in acc int min_x, min_y, max_x, max_y; ImageBoundingBox(img, M, min_x, min_y, max_x, max_y); CVector3 p; double newx, newy; // Exposure Compensation double lumaScale = 1.0; double lumaAcc = 0.0; double lumaImg = 0.0; int cnt = 0; for (int ii = min_x; ii < max_x; ii++) for (int jj = min_y; jj < max_y; jj++) { // flag: current pixel black or not bool flag = false; p[0] = ii; p[1] = jj; p[2] = 1; p = M.Inverse() * p; newx = p[0] / p[2]; newy = p[1] / p[2]; // If in the overlapping region if (newx >=0 && newx < width && newy >=0 && newy < height) { if (acc.Pixel(ii,jj,0) == 0 && acc.Pixel(ii,jj,1) == 0 && acc.Pixel(ii,jj,2) == 0) flag = true; if (img.PixelLerp(newx,newy,0) == 0 && img.PixelLerp(newx,newy,1) == 0 && img.PixelLerp(newx,newy,2) == 0) flag = true; if (!flag) { // Compute Y using RGB (RGB -> YUV) lumaAcc = 0.299 * acc.Pixel(ii,jj,0) + 0.587 * acc.Pixel(ii,jj,1) + 0.114 * acc.Pixel(ii,jj,2); lumaImg = 0.299 * img.PixelLerp(newx,newy,0) + 0.587 * img.PixelLerp(newx,newy,1) + 0.114 * img.PixelLerp(newx,newy,2); if (lumaImg != 0) { double scale = lumaAcc / lumaImg; if (scale > 0.5 && scale < 2) { lumaScale += lumaAcc / lumaImg; cnt++; } } } } } if (cnt != 0) lumaScale = lumaScale / (double)cnt; else lumaScale = 1.0; // add every pixel in img to acc, feather the region withing blendwidth to the bounding box, // pure black pixels (caused by warping) are not added double weight; for (int ii = min_x; ii < max_x; ii++) for (int jj = min_y; jj < max_y; jj++) { p[0] = ii; p[1] = jj; p[2] = 1; p = M.Inverse() * p; newx = p[0] / p[2]; newy = p[1] / p[2]; if ((newx >= 0) && (newx < width-1) && (newy >= 0) && (newy < height-1)) { weight = 1.0; if ( (ii >= min_x) && (ii < (min_x+blendWidth)) ) weight = (ii-min_x) / blendWidth; if ( (ii <= max_x) && (ii > (max_x-blendWidth)) ) weight = (max_x-ii) / blendWidth; if (img.Pixel(iround(newx),iround(newy),0) == 0 && img.Pixel(iround(newx),iround(newy),1) == 0 && img.Pixel(iround(newx),iround(newy),2) == 0) weight = 0.0; double LerpR = img.PixelLerp(newx, newy, 0); double LerpG = img.PixelLerp(newx, newy, 1); double LerpB = img.PixelLerp(newx, newy, 2); double r = LerpR*lumaScale > 255.0 ? 255.0 : LerpR*lumaScale; double g = LerpG*lumaScale > 255.0 ? 255.0 : LerpG*lumaScale; double b = LerpB*lumaScale > 255.0 ? 255.0 : LerpB*lumaScale; acc.Pixel(ii,jj,0) += r * weight; acc.Pixel(ii,jj,1) += g * weight; acc.Pixel(ii,jj,2) += b * weight; acc.Pixel(ii,jj,3) += weight; } } printf("AccumulateBlend\n"); // END TODO }
Feature HOGFeatureExtractor::operator()(const CByteImage& img_) const { /******** BEGIN TODO ********/ // Compute the Histogram of Oriented Gradients feature // Steps are: // 1) Compute gradients in x and y directions. We provide the // derivative kernel proposed in the paper in _kernelDx and // _kernelDy. // 2) Compute gradient magnitude and orientation // 3) Add contribution each pixel to HOG cells whose // support overlaps with pixel. Each cell has a support of size // _cellSize and each histogram has _nAngularBins. // 4) Normalize HOG for each cell. One simple strategy that is // is also used in the SIFT descriptor is to first threshold // the bin values so that no bin value is larger than some // threshold (we leave it up to you do find this value) and // then re-normalize the histogram so that it has norm 1. A more // elaborate normalization scheme is proposed in Dalal & Triggs // paper but we leave that as extra credit. // // Useful functions: // convertRGB2GrayImage, TypeConvert, WarpGlobal, Convolve int xCells = ceil(1.*img_.Shape().width / _cellSize); int yCells = ceil(1.*img_.Shape().height / _cellSize); CFloatImage HOGHist(xCells, yCells, _nAngularBins); HOGHist.ClearPixels(); CByteImage gray(img_.Shape()); CFloatImage grayF(img_.Shape().width, img_.Shape().height, 1); convertRGB2GrayImage(img_, gray); TypeConvert(gray, grayF); CFloatImage diffX( img_.Shape()), diffY( img_.Shape()); Convolve(grayF, diffX, _kernelDx); Convolve(grayF, diffY, _kernelDy); CFloatImage grad(grayF.Shape()), grad2(grayF.Shape()); CFloatImage angl(grayF.Shape()), angl2(grayF.Shape()); for (int y = 0; y <grayF.Shape().height; y++){ for (int x = 0; x<grayF.Shape().width; x++) { grad2.Pixel(x,y,0) = (diffX.Pixel(x,y,0) * diffX.Pixel(x,y,0) + diffY.Pixel(x,y,0) * diffY.Pixel(x,y,0)); angl2.Pixel(x,y,0) = atan(diffY.Pixel(x,y,0) / abs(diffY.Pixel(x,y,0))); } } // Bilinear Filter ConvolveSeparable(grad2, grad, ConvolveKernel_121,ConvolveKernel_121,1); ConvolveSeparable(angl2, angl, ConvolveKernel_121,ConvolveKernel_121,1); //WriteFile(diffX, "angle.tga"); //WriteFile(diffY, "angleG.tga"); for (int y = 0; y <grayF.Shape().height; y++){ for (int x = 0; x<grayF.Shape().width; x++) { // Fit in the bins int a = angl.Pixel(x,y,0) / 3.14 * (_nAngularBins) + _nAngularBins/2; // Histogram HOGHist.Pixel(floor(1.*x / _cellSize), floor(1.*y / _cellSize), a) += grad.Pixel(x,y,0); } } // Normalization float threshold = 0.7; for (int y = 0; y < yCells; y++){ for (int x = 0; x < xCells; x++){ float total = 0; for (int a = 0; a < _nAngularBins; a++) { if (HOGHist.Pixel(x,y,a) > threshold) HOGHist.Pixel(x,y,a) = threshold; // Sum for normalization total += HOGHist.Pixel(x,y,a); } for (int a = 0;a< _nAngularBins; a++) { HOGHist.Pixel(x,y,a) /= total; } } } return HOGHist; /******** END TODO ********/ }
// TODO: the following function should go somewhere else, perhaps in ImageIO.cpp // Make sure the image has the smallest number of bands before writing. // That is, if it's 4 bands with full alpha, reduce to 3 bands. // If it's 3 bands with constant colors, make it 1-band. CByteImage removeRedundantBands(CByteImage img) { CShape sh = img.Shape(); int w = sh.width, h = sh.height, nB = sh.nBands; int x, y; if (nB < 3) return img; // check if full alpha if alpha channel present bool fullAlpha = true; if (nB == 4) { for (y = 0; y < h && fullAlpha; y++) { uchar *pix = &img.Pixel(0, y, 0); for (x = 0; x < w; x++) { if (pix[3] != 255) { fullAlpha = false; break; } pix += nB; } } } if (!fullAlpha) return img; // check for equal colors bool equalColors = true; for (y = 0; y < h && equalColors; y++) { uchar *pix = &img.Pixel(0, y, 0); for (x = 0; x < w; x++) { if (pix[0] != pix[1] || pix[0] != pix[2] || pix[1] != pix[2]) { equalColors = false; break; } pix += nB; } } // at this point, if nB == 4 we can reduce to at least 3 bands, // and if equalColors we can reduce to 1 band. if (! equalColors && nB < 4) return img; int newNB = equalColors ? 1 : 3; if (DEBUG_ImageIOpng) fprintf(stderr, "reducing from %d to %d bands\n", nB, newNB); CShape sh2(w, h, newNB); CByteImage img2(sh2); for (y = 0; y < h; y++) { uchar *pix = &img.Pixel(0, y, 0); uchar *pix2 = &img2.Pixel(0, y, 0); for (x = 0; x < w; x++) { for (int b = 0; b < newNB; b++) { pix2[b] = pix[b]; } pix += nB; pix2 += newNB; } } return img2; }
void WriteFilePNG(CByteImage img, const char* filename) { img = removeRedundantBands(img); CShape sh = img.Shape(); int width = sh.width, height = sh.height, nBands = sh.nBands; // Make sure the image has the smallest number of bands before writing. // That is, if it's 4 bands with full alpha, reduce to 3 bands. // If it's 3 bands with constant colors, make it 1-band. FILE *stream = fopen(filename, "wb"); if (stream == 0) throw CError("WriteFilePNG: could not open %s", filename); png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)pngfile_error, (png_error_ptr)NULL); if (!png_ptr) { fclose(stream); throw CError("WriteFilePNG: error creating png structure"); } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { fclose(stream); throw CError("WriteFilePNG: error creating png structure"); } png_init_io(png_ptr, stream); int bits = 8; int colortype = nBands == 1 ? PNG_COLOR_TYPE_GRAY : nBands == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; png_set_IHDR(png_ptr, info_ptr, width, height, bits, colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // write the file header information png_write_info(png_ptr, info_ptr); // swap the BGR pixels in the DiData structure to RGB png_set_bgr(png_ptr); // allocate a vector of row pointers std::vector<uchar *> rowPtrs; rowPtrs.resize(height); for (int y = 0; y<height; y++) rowPtrs[y] = &img.Pixel(0, y, 0); // write the whole image png_write_image(png_ptr, &rowPtrs[0]); // write the additional chunks to the PNG file (not really needed) png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, (png_infopp) NULL); fclose (stream); }
int BlendPairs(int argc, const char *argv[]) { // Blend a sequence of images given the pairwise transformations if (argc < 5) { printf("usage: %s pairlist.txt outimg.tga blendWidth\n", argv[1]); return -1; } const char *pairlist= argv[2]; const char *outfile = argv[3]; float blendWidth = (float) atof(argv[4]); // Open the list of image pairs FILE *stream = fopen(pairlist, "r"); if (stream == 0) throw CError("%s: could not open the file %s", argv[1], pairlist); // Construct the list of images and translations CImagePositionV ipList; char line[1024], infile1[1024], infile2[1024]; // float rel_t[2]; CTransform3x3 M; int n; for (n = 0; fgets(line, 1024, stream); n++) { // Compute the position from the PREVIOUS displacement CImagePosition ip; if (n == 0) { ip.position = CTransform3x3::Translation(0.0, 0.0); /* Ident matrix */ // ip.position = CTransform3x3::Rotation(45.0); /* Ident matrix */ } else { ip.position = M * ipList[n-1].position; } // for (int k = 0; k < 2; k++) // ip.position[k] = (n > 0) ? ipList[n-1].position[k] - rel_t[k] : 0.0f; // Read the line and push the image onto the list // if (sscanf(line, "%s %s %f %f", infile1, infile2, // &rel_t[0], &rel_t[1]) != 4) // throw CError("%s: error reading %s", argv[1], pairlist); #if 0 if (sscanf(line, "%s %s %lf %lf", infile1, infile2, &(M[0][2]), &(M[1][2])) != 4) throw CError("%s: error reading %s\n", argv[1], pairlist); #else if (sscanf(line, "%s %s %lf %lf %lf %lf %lf %lf %lf %lf %lf", infile1, infile2, &(M[0][0]), &(M[0][1]), &(M[0][2]), &(M[1][0]), &(M[1][1]), &(M[1][2]), &(M[2][0]), &(M[2][1]), &(M[2][2])) != 11) throw CError("%s: error reading %s\n", argv[1], pairlist); #endif // The coordinate system of the ImageLib images has y reflected -- so let's fix that! // M[1][2] = -M[1][2]; ip.imgName = std::string(infile1); CByteImage imgTmp; ReadFile(imgTmp, infile1); ip.img = CByteImage(imgTmp.Shape()); FlipImage(imgTmp, ip.img); ipList.push_back(ip); } // Read in the last image CImagePosition ip; // for (int k = 0; k < 2; k++) // ip.position[k] = ipList[n-1].position[k] - rel_t[k]; ip.position = M * ipList[n-1].position; ip.imgName = std::string(infile2); CByteImage imgTmp; ReadFile(imgTmp, infile2); ip.img = CByteImage(imgTmp.Shape()); FlipImage(imgTmp, ip.img); ipList.push_back(ip); fclose(stream); CByteImage resultTmp = BlendImages(ipList, blendWidth); // Flip again CByteImage result(resultTmp.Shape()); FlipImage(resultTmp, result); WriteFile(result, outfile); return 0; }