Created
June 10, 2011 01:32
-
-
Save rharjes/1018104 to your computer and use it in GitHub Desktop.
Proof-of-concept photo mosaic builder (kinda fragile at the moment)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Photo Mosaic builder | |
// Author: Robbie Harjes | |
// Date: 6/9/2011 | |
// Notes: Free for non-commercial projects. This notice must | |
// remain intact if you use this code. | |
// 1. Image library (directory) is loaded from first command-line argument | |
// This argument needs to end with: \* to work correctly. | |
// Images need to be 50x50 pixels. | |
// 2. This will only work on Windows (directory searching) | |
// 3. Not all images will get utilized, I will fix this | |
#include <windows.h> | |
#include <tchar.h> | |
#include <stdio.h> | |
#include <cmath> | |
#include "cv.h" | |
#include "highgui.h" | |
#include "iostream" | |
#include "stdlib.h" | |
CvScalar averageValue(const IplImage* src); | |
double computeErrorValue(CvScalar a, CvScalar b); | |
using namespace std; | |
int main(int argc, char* argv[]) | |
{ | |
WIN32_FIND_DATA FindFileData; | |
HANDLE hFind = FindFirstFile(argv[1], &FindFileData); // . | |
FindNextFile(hFind, &FindFileData); // .. | |
// Load the collection of thumbnails, and compute their average color scalars. | |
IplImage* img_thumbs[88]; | |
CvScalar s_thumbs[88]; | |
int used[88]; | |
for(int i = 0; i < 88; i++) { | |
used[i] = 0; | |
} | |
cout << "Loading image library in: " << argv[1] << "..." << endl; | |
for (int i = 0; FindNextFile(hFind, &FindFileData) != 0; i++) { | |
char thumb_name[256]; | |
strcpy(thumb_name, argv[1]); | |
thumb_name[strlen(thumb_name)-1] = '\0'; | |
strcat(thumb_name, (char*)FindFileData.cFileName); | |
img_thumbs[i] = cvLoadImage((const char*)thumb_name); | |
s_thumbs[i] = averageValue(img_thumbs[i]); | |
} | |
// Load our input image, and expand it 1000%. | |
IplImage* sourceImage = cvLoadImage("source.jpg"); | |
cout << "Loaded source image: " << sourceImage->width << "x" << sourceImage->height << endl; | |
IplImage* outImage = cvCreateImage(cvSize(sourceImage->width*10, sourceImage->height*10), sourceImage->depth, sourceImage->nChannels); | |
cvResize(sourceImage, outImage); | |
// Process the input image | |
cout << "Building mosaic..."; | |
for (int y = 0; y < outImage->height; y += 50) { | |
for (int x = 0; x < outImage->width; x += 50) { | |
// Initialize our Region-Of-Interest | |
cvSetImageROI(outImage, cvRect(x,y,50,50)); | |
// Compute average "color" (Scalar) | |
CvScalar s_target = averageValue(outImage); | |
// Search for closest match in our thumbnail scalars. | |
// e.g. minimize the error value between the two regions, | |
// keeping track of some index into the thumbnail array. | |
int best_thumb_index = -1; // Invalid | |
double min_error_so_far = 195076; // MAX = (255^2)*3 + 1 | |
for(int i = 0; i < 88; i++) { | |
double errorValue = computeErrorValue(s_target, s_thumbs[i]); | |
if (errorValue < min_error_so_far) { | |
min_error_so_far = errorValue; | |
best_thumb_index = i; | |
} | |
} | |
// Copy the corresponding thumbnail into the final image. | |
cvCopy(img_thumbs[best_thumb_index], outImage); | |
used[best_thumb_index] = 1; | |
// Reset the Region-Of-Interest | |
cvResetImageROI(outImage); | |
} | |
} | |
cout << "Done!\n" << endl; | |
double utilization = 0.0; | |
for (int i = 0; i < 88; i++) { | |
utilization += used[i]; | |
} | |
cout << "Image library utilization: " << utilization/88*100 << "%" << endl; | |
cvResize(outImage, sourceImage); | |
//cvSaveImage("mosaic_full.png", outImage); // ~50-100 MBs | |
//cvSaveImage("mosaic_default.png", sourceImage); // < 1 MB | |
cvNamedWindow("Processed image", CV_WINDOW_AUTOSIZE); | |
cvShowImage("Processed image", sourceImage); | |
cvWaitKey(0); | |
cvDestroyWindow("Processed image"); | |
cvReleaseImage(&sourceImage); | |
cvReleaseImage(&outImage); | |
for(int i = 0; i < 88; i++) { | |
cvReleaseImage(&img_thumbs[i]); | |
} | |
return 0; | |
} | |
// Calculates the sum of square differences between two scalars. | |
double computeErrorValue(CvScalar a, CvScalar b) { | |
return abs(a.val[0] - b.val[0])*abs(a.val[0] - b.val[0]) + | |
abs(a.val[1] - b.val[1])*abs(a.val[1] - b.val[1]) + | |
abs(a.val[2] - b.val[2])*abs(a.val[2] - b.val[2]); | |
} | |
// Calculate the average value of an IplImage* region. | |
CvScalar averageValue(const IplImage* src) { | |
int nPixels = 50*50; | |
double b = 0; | |
double g = 0; | |
double r = 0; | |
for(int y = 0; y < 50; y++) { | |
for (int x = 0; x < 50; x++) { | |
CvScalar s = cvGet2D(src, x, y); | |
b += s.val[0]; | |
g += s.val[1]; | |
r += s.val[2]; | |
} | |
} | |
b = b / nPixels; | |
g = g / nPixels; | |
r = r / nPixels; | |
return cvScalar(b, g, r); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment