/******************* TO DO ********************* * leastSquaresFit: * INPUT: * f1, f2: source feature sets * matches: correspondences between f1 and f2 * m: motion model * inliers: inlier match indices (indexes into 'matches' array) * M: transformation matrix (output) * OUTPUT: * compute the transformation from f1 to f2 using only the inliers * and return it in M */ int leastSquaresFit(const FeatureSet &f1, const FeatureSet &f2, const vector<FeatureMatch> &matches, MotionModel m, const vector<int> &inliers, CTransform3x3& M) { // This function needs to handle two possible motion models, // This function needs to handle two possible motion models, // pure translations and full homographies. switch (m) { case eTranslate: { // for spherically warped images, the transformation is a // for spherically warped images, the transformation is a // translation and only has two degrees of freedom // // therefore, we simply compute the average translation vector // between the feature in f1 and its match in f2 for all inliers double u = 0; double v = 0; for (int i=0; i < (int) inliers.size(); i++) { // BEGIN TODO // use this loop to compute the average translation vector // over all inliers int m1 = matches.at(inliers.at(i)).id1; int m2 = matches.at(inliers.at(i)).id2; u += f1[m1].x - f2[m2].x; v += f1[m1].y - f2[m2].y; printf("TODO: %s:%d\n", __FILE__, __LINE__); // END TODO } u /= inliers.size(); v /= inliers.size(); M = CTransform3x3::Translation((float) u, (float) v); break; } case eHomography: { M = CTransform3x3(); // BEGIN TODO // Compute a homography M using all inliers. // This should call ComputeHomography. vector<FeatureMatch> inmatches; for (int bo = 0; bo < inliers.size(); bo++) { FeatureMatch mat; mat = matches.at(inliers.at(bo)); inmatches.push_back(mat); } M = ComputeHomography(f1, f2, inmatches); break; } case eRotate3D: { cout << "3D Rotation is not supported by this project"; break; } } // END TODO return 0; }
// Compute MOPs descriptors. void ComputeMOPSDescriptors(CFloatImage &image, FeatureSet &features) { CFloatImage grayImage=ConvertToGray(image); CFloatImage blurredImage; Convolve(grayImage, blurredImage, ConvolveKernel_7x7); CFloatImage postHomography = CFloatImage(); CFloatImage gaussianImage = GetImageFromMatrix((float *)gaussian5x5Float, 5, 5); //first make the image invariant to changes in illumination by subtracting off the mean int grayHeight = grayImage.Shape().height; int grayWidth = grayImage.Shape().width; // now make this rotation invariant vector<Feature>::iterator featureIterator = features.begin(); while (featureIterator != features.end()) { Feature &f = *featureIterator; CTransform3x3 scaleTransform = CTransform3x3(); CTransform3x3 translationNegative; CTransform3x3 translationPositive; CTransform3x3 rotation; double scaleFactor = 41/8; scaleTransform[0][0] = scaleFactor; scaleTransform[1][1] = scaleFactor; translationNegative = translationNegative.Translation(f.x,f.y); translationPositive = translationPositive.Translation(-4, -4); rotation = rotation.Rotation(f.angleRadians * 180/ PI); CTransform3x3 finalTransformation = translationNegative * rotation * scaleTransform * translationPositive; //CFloatImage sample61x61Window = //CFloatImage pixelWindow = GetXWindowAroundPixel(grayImage, f.x, f.y, 61); WarpGlobal(blurredImage, postHomography, finalTransformation, eWarpInterpLinear, 1.0f); //now we get the 41x41 box around the feature for(int row=0; row< 8; row++) { for(int col=0;col< 8;col++) { f.data.push_back(postHomography.Pixel(col, row, 0)); } } /* // now we do the subsampling first round to reduce to a 20x20 int imgSize = 41; subsample(&f, imgSize, gaussianImage); //second round of subsampling to get it to a 10x10 imgSize = 20; subsample(&f, imgSize, gaussianImage); imgSize = 10; CFloatImage img = featureToImage(f, imgSize, imgSize); CFloatImage blurredImg(img.Shape()); Convolve(img, blurredImg, gaussianImage); featuresFromImage(&f,blurredImg,imgSize,imgSize); int count = 0; for(int y=0; y<imgSize; y++) { for(int x=0; x<imgSize; x++) { if(x == 3 || x == 7 || y == 3 || y == 7) { f.data.erase(f.data.begin() + count); } else { count++; } } } */ normalizeIntensities(&f, 8, 8); featureIterator++; } }
/******************* TO DO 5 ********************* * BlendImages: * INPUT: * ipv: list of input images and their relative positions in the mosaic * blendWidth: width of the blending function * OUTPUT: * create & return final mosaic by blending all images * and correcting for any vertical drift */ CByteImage BlendImages(CImagePositionV& ipv, float blendWidth) { // Assume all the images are of the same shape (for now) CByteImage& img0 = ipv[0].img; CShape sh = img0.Shape(); int width = sh.width; int height = sh.height; int nBands = sh.nBands; int dim[2] = {width, height}; // Compute the bounding box for the mosaic int n = ipv.size(); float min_x = 0, min_y = 0; float max_x = 0, max_y = 0; int i; float dy = 0; for (i = 0; i < n; i++) { CTransform3x3 &pos = ipv[i].position; CVector3 corners[4];//表示图片的4个角的坐标,分别为左下,右下,左上,右上 corners[0][0] = 0.0; corners[0][1] = 0.0; corners[0][2] = 1.0; corners[1][0] = width - 1; corners[1][1] = 0.0; corners[1][2] = 1.0; corners[2][0] = 0.0; corners[2][1] = height - 1; corners[2][2] = 1.0; corners[3][0] = width - 1; corners[3][1] = height - 1; corners[3][2] = 1.0; corners[0] = pos * corners[0]; corners[1] = pos * corners[1]; corners[2] = pos * corners[2]; corners[3] = pos * corners[3]; corners[0][0] /= corners[0][2]; corners[0][1] /= corners[0][2]; corners[1][0] /= corners[0][2]; corners[1][1] /= corners[0][2]; corners[2][0] /= corners[0][2]; corners[2][1] /= corners[0][2]; corners[3][0] /= corners[0][2]; corners[3][1] /= corners[0][2]; // *** BEGIN TODO #1 *** // add some code here to update min_x, ..., max_y //Use c0 and c3 to get the range of x and y. int iminx, iminy, imaxx, imaxy; ImageBoundingBox(img0, pos, iminx, iminy, imaxx, imaxy); if (i == 0) { dy += imaxy; } if (i == n - 1) { dy -= imaxy; } /*if (min_x > corners[0][0]) { min_x = corners[0][0]; } if (max_x < corners[3][0]) { max_x = corners[3][0]; } if (min_y > corners[0][1]) { min_y = corners[0][1]; } if (max_y < corners[3][1]) { max_y = corners[3][1]; } */ min_x = min(min_x, float(iminx)); min_y = min(min_y, float(iminy)); max_x = max(max_x, float(imaxx)); max_y = max(max_y, float(imaxy)); // *** END TODO #1 *** } // Create a floating point accumulation image CShape mShape((int)(ceil(max_x) - floor(min_x)), (int)(ceil(max_y) - floor(min_y)), nBands); CFloatImage accumulator(mShape); accumulator.ClearPixels(); double x_init, x_final; double y_init, y_final; // Add in all of the images for (i = 0; i < n; i++) { CTransform3x3 &M = ipv[i].position; CTransform3x3 M_t = CTransform3x3::Translation(-min_x, -min_y) * M; CByteImage& img = ipv[i].img; // Perform the accumulation AccumulateBlend(img, accumulator, M_t, blendWidth); if (i == 0) { CVector3 p; p[0] = 0.5 * width; p[1] = 0.0; p[2] = 1.0; p = M_t * p; x_init = p[0]; y_init = p[1]; } else if (i == n - 1) { CVector3 p; p[0] = 0.5 * width; p[1] = 0.0; p[2] = 1.0; p = M_t * p; x_final = p[0]; y_final = p[1]; } } // Normalize the results CByteImage compImage(mShape); NormalizeBlend(accumulator, compImage); bool debug_comp = false; if (debug_comp) WriteFile(compImage, "tmp_comp.tga"); // Allocate the final image shape CShape cShape(mShape.width - width, height, nBands); CByteImage croppedImage(cShape); // Compute the affine deformation CTransform3x3 A = CTransform3x3(); // *** BEGIN TODO #2 *** // fill in the right entries in A to trim the left edge and // to take out the vertical drift A[0][2] = width /2; A[1][0] = dy / (mShape.width - width); // *** END TODO #2 *** // Warp and crop the composite WarpGlobal(compImage, croppedImage, A, eWarpInterpLinear); // WarpGlobal(compImage, croppedImage, A, eWarpInterpNearest); //similar as linear // WarpGlobal(compImage, croppedImage, A, eWarpInterpCubic); //all pixels are black return croppedImage; }
/******************* TO DO 5 ********************* * BlendImages: * INPUT: * ipv: list of input images and their relative positions in the mosaic * blendWidth: width of the blending function * OUTPUT: * create & return final mosaic by blending all images * and correcting for any vertical drift */ CByteImage BlendImages(CImagePositionV& ipv, float blendWidth) { // Assume all the images are of the same shape (for now) CByteImage& img0 = ipv[0].img; CShape sh = img0.Shape(); int width = sh.width; int height = sh.height; int nBands = sh.nBands; // int dim[2] = {width, height}; int n = ipv.size(); if (n == 0) return CByteImage(0,0,1); bool is360 = false; // Hack to detect if this is a 360 panorama if (ipv[0].imgName == ipv[n-1].imgName) is360 = true; // Compute the bounding box for the mosaic float min_x = FLT_MAX, min_y = FLT_MAX; float max_x = 0, max_y = 0; int i; for (i = 0; i < n; i++) { CTransform3x3 &T = ipv[i].position; // BEGIN TODO // add some code here to update min_x, ..., max_y printf("TODO: %s:%d\n", __FILE__, __LINE__); // END TODO } // Create a floating point accumulation image CShape mShape((int)(ceil(max_x) - floor(min_x)), (int)(ceil(max_y) - floor(min_y)), nBands + 1); CFloatImage accumulator(mShape); accumulator.ClearPixels(); double x_init, x_final; double y_init, y_final; // Add in all of the images for (i = 0; i < n; i++) { // Compute the sub-image involved CTransform3x3 &M = ipv[i].position; CTransform3x3 M_t = CTransform3x3::Translation(-min_x, -min_y) * M; CByteImage& img = ipv[i].img; // Perform the accumulation AccumulateBlend(img, accumulator, M_t, blendWidth); if (i == 0) { CVector3 p; p[0] = 0.5 * width; p[1] = 0.0; p[2] = 1.0; p = M_t * p; x_init = p[0]; y_init = p[1]; } else if (i == n - 1) { CVector3 p; p[0] = 0.5 * width; p[1] = 0.0; p[2] = 1.0; p = M_t * p; x_final = p[0]; y_final = p[1]; } } // Normalize the results mShape = CShape((int)(ceil(max_x) - floor(min_x)), (int)(ceil(max_y) - floor(min_y)), nBands); CByteImage compImage(mShape); NormalizeBlend(accumulator, compImage); bool debug_comp = false; if (debug_comp) WriteFile(compImage, "tmp_comp.tga"); // Allocate the final image shape int outputWidth = 0; if (is360) { outputWidth = mShape.width - width; } else { outputWidth = mShape.width; } CShape cShape(outputWidth, mShape.height, nBands); CByteImage croppedImage(cShape); // Compute the affine transformation CTransform3x3 A = CTransform3x3(); // identify transform to initialize // BEGIN TODO // fill in appropriate entries in A to trim the left edge and // to take out the vertical drift if this is a 360 panorama // (i.e. is360 is true) printf("TODO: %s:%d\n", __FILE__, __LINE__); // END TODO // Warp and crop the composite WarpGlobal(compImage, croppedImage, A, eWarpInterpLinear); return croppedImage; }