Skip to content

Instantly share code, notes, and snippets.

@rharjes
Created June 10, 2011 01:32
Show Gist options
  • Save rharjes/1018104 to your computer and use it in GitHub Desktop.
Save rharjes/1018104 to your computer and use it in GitHub Desktop.
Proof-of-concept photo mosaic builder (kinda fragile at the moment)
// 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