Permalink
Browse files

implementation of debevec's gamma calibration routine gsolve, ported …

…from matlab. this is based on an earlier implementation i did for ofxStructuredLight, but the old version had some bugs and should be considered deprecated.
  • Loading branch information...
1 parent b21d340 commit 314da73def82535b8ac2b14c8662e7d3fcfbfcd8 @kylemcdonald kylemcdonald committed Oct 30, 2011

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -0,0 +1,9 @@
+//THE PATH TO THE ROOT OF OUR OF PATH RELATIVE TO THIS PROJECT.
+//THIS NEEDS TO BE DEFINED BEFORE CoreOF.xcconfig IS INCLUDED
+OF_PATH = ../../..
+
+//THIS HAS ALL THE HEADER AND LIBS FOR OF CORE
+#include "../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig"
+
+OTHER_LDFLAGS = $(OF_CORE_LIBS)
+HEADER_SEARCH_PATHS = $(OF_CORE_HEADERS) "../../../addons/ofxOpenCv/libs/opencv/include/"
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.openFrameworks</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+</dict>
+</plist>
@@ -0,0 +1,132 @@
+#include "GammaCalibration.h"
+
+void GammaCalibration::calibrate(vector<ofImage>& images, vector<float>& shutterTimes,
+ vector<float>& grayGamma,
+ int sampledLocations, float smoothness) {
+ calibrate(images, shutterTimes, grayGamma, 0, 1, sampledLocations, smoothness);
+}
+
+void GammaCalibration::calibrate(vector<ofImage>& images, vector<float>& shutterTimes,
+ vector<float>& redGamma, vector<float>& greenGamma, vector<float>& blueGamma,
+ int sampledLocations, float smoothness) {
+ calibrate(images, shutterTimes, redGamma, 0, 3, sampledLocations, smoothness);
+ calibrate(images, shutterTimes, greenGamma, 1, 3, sampledLocations, smoothness);
+ calibrate(images, shutterTimes, blueGamma, 2, 3, sampledLocations, smoothness);
+}
+
+void GammaCalibration::calibrate(vector<ofImage>& images, vector<float>& shutterTimes,
+ vector<float>& gamma,
+ int offset, int step,
+ int sampledLocations, float smoothness) {
+ // take the log of the shutterTimes
+ int sampledshutters = images.size();
+ CvMat* inputshutters = cvCreateMat(sampledshutters, 1, CV_32FC1);
+ float* inputshuttersPtr = (float*) cvPtr1D(inputshutters, 0);
+ for(int i = 0; i < shutterTimes.size(); i++)
+ inputshuttersPtr[i] = logf(shutterTimes[i]);
+
+ // allocate working space
+ CvMat* inputCurves = cvCreateMat(sampledLocations, sampledshutters, CV_8UC1);
+ CvMat* weighting = cvCreateMat(levels, 1, CV_8UC1);
+ int* locations = new int[sampledLocations];
+
+ // allocate the linear system
+ long sizeA1 = sampledLocations * sampledshutters + levels + 1;
+ long sizeA2 = levels + sampledLocations;
+ CvMat* A = cvCreateMat(sizeA1, sizeA2, CV_32FC1);
+ CvMat* b = cvCreateMat(sizeA1, 1, CV_32FC1);
+ CvMat* x = cvCreateMat(sizeA2, 1, CV_32FC1);
+ float* xptr = (float*) cvPtr1D(x, 0);
+
+ // histogram the intensity values of the middle image
+ ofImage& middleImage = images[images.size() / 2];
+ vector< vector<int> > histogram(levels);
+ int totalPixels = (int) middleImage.getWidth() * (int) middleImage.getHeight();
+ unsigned char* middle = middleImage.getPixels();
+ for(int i = 0; i < totalPixels; i++)
+ histogram[middle[i * step + offset]].push_back(i);
+
+ // find nonempty histogram bins
+ vector<unsigned char> nonempty;
+ for(int i = 0; i < levels; i++)
+ if(histogram[i].size() > 0)
+ nonempty.push_back(i);
+
+ // from nonempty bins, pick random shutter-equidistant locations
+ for(int i = 0; i < sampledLocations; i++) {
+ int intensity = nonempty[(i * nonempty.size()) / sampledLocations];
+ vector<int>& bin = histogram[intensity];
+ locations[i] = bin[(int) ofRandom(0, bin.size())];
+ }
+
+ // given the locations, fill up the input curves
+ unsigned char* inputCurvesPtr = (unsigned char*) cvPtr2D(inputCurves, 0, 0);
+ for(int i = 0; i < sampledLocations; i++) {
+ int curLocation = locations[i] * step + offset;
+ for(int j = 0; j < sampledshutters; j++) {
+ unsigned char* curPixels = images[j].getPixels();
+ *(inputCurvesPtr++) = curPixels[curLocation];
+ }
+ }
+
+ // generate the weighting (hat) function
+ unsigned char* weightingPtr = (unsigned char*) cvPtr1D(weighting, 0);
+ for(int z = 0; z < levels / 2; z++)
+ weightingPtr[z] = z;
+ for(int z = levels / 2; z < levels; z++)
+ weightingPtr[z] = levels - z;
+
+ // fill up the system: data fitting
+ int k = 0;
+ for(int i = 0; i < sampledLocations; i++) {
+ for(int j = 0; j < sampledshutters; j++) {
+ unsigned char curZ = *cvPtr2D(inputCurves, i, j);
+ unsigned char wij = weightingPtr[curZ];
+ *((float*) cvPtr2D(A, k, curZ)) = wij;
+ *((float*) cvPtr2D(A, k, levels + i)) = -wij;
+ *((float*) cvPtr1D(b, k)) = wij * inputshuttersPtr[j];
+ k++;
+ }
+ }
+
+ // fill up the system: set the middle of the curve to 0
+ *((float*) cvPtr2D(A, k, 128)) = 0;
+ k++;
+
+ // fill up the system: include smoothness constraint
+ for(int i = 0; i < levels - 2; i++) {
+ *((float*) cvPtr2D(A, k, i)) = smoothness * weightingPtr[i];
+ *((float*) cvPtr2D(A, k, i + 1)) = -2 * smoothness * weightingPtr[i];
+ *((float*) cvPtr2D(A, k, i + 2)) = smoothness * weightingPtr[i];
+ k++;
+ }
+
+ // solve the system
+ cvSolve(A, b, x, CV_SVD);
+
+ gamma.clear();
+ gamma.resize(256);
+
+ // load gamma from xptr
+ for(int i = 0; i < levels; i++) {
+ float cur = expf(xptr[i]);
+ gamma[i] += cur;
+ }
+
+ // normalize to range
+ float top = gamma[255];
+ for(int i = 0; i < levels; i++)
+ gamma[i] = (gamma[i] * 255) / top;
+ //gamma[0] = 0;
+
+ // clear all the allocated memory
+ delete [] locations;
+
+ cvReleaseMat(&A);
+ cvReleaseMat(&b);
+ cvReleaseMat(&x);
+
+ cvReleaseMat(&inputCurves);
+ cvReleaseMat(&inputshutters);
+ cvReleaseMat(&weighting);
+}
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "ofxOpenCv.h"
+#include <fstream>
+
+class GammaCalibration {
+public:
+ static void calibrate(vector<ofImage>& images, vector<float>& shutterTimes,
+ vector<float>& grayGamma,
+ int sampledLocations = 45, float smoothness = 1);
+
+ static void calibrate(vector<ofImage>& images, vector<float>& shutterTimes,
+ vector<float>& redGamma, vector<float>& greenGamma, vector<float>& blueGamma,
+ int sampledLocations = 45, float smoothness = 1);
+
+private:
+ static const int levels = 256;
+
+ static void calibrate(vector<ofImage>& images, vector<float>& shutterTimes,
+ vector<float>& gamma,
+ int offset, int step,
+ int sampledLocations, float smoothness);
+};
@@ -0,0 +1,67 @@
+#include "LutFilter.h"
+
+LutFilter::LutFilter() {
+}
+
+void LutFilter::setup(string filename) {
+ ifstream file;
+ file.open(ofToDataPath(filename).c_str());
+ if(file.is_open()) {
+ string line;
+ int level = 0;
+ while (!file.eof()) {
+ getline(file, line);
+ vector<string> split;
+ tokenize(line, split, ",");
+ if(split.size() == 3) {
+ type = OF_IMAGE_COLOR;
+ for(int i = 0; i < 3; i++)
+ rgbGamma[level][i] = toInt(split[i]);
+ } else if(split.size() == 1) {
+ type = OF_IMAGE_GRAYSCALE;
+ grayGamma[level] = toInt(split[0]);
+ }
+ level++;
+ }
+ file.close();
+ }
+}
+
+void LutFilter::tokenize(const string& str, vector<string>& tokens, const string& delimiters) {
+ int lastPos = str.find_first_not_of(delimiters, 0);
+ int pos = str.find_first_of(delimiters, lastPos);
+ while (string::npos != pos || string::npos != lastPos) {
+ tokens.push_back(str.substr(lastPos, pos - lastPos));
+ lastPos = str.find_first_not_of(delimiters, pos);
+ pos = str.find_first_of(delimiters, lastPos);
+ }
+}
+
+int LutFilter::toInt(string str) {
+ stringstream stream;
+ stream << str;
+ int out;
+ stream >> out;
+ return out;
+}
+
+void LutFilter::filter(ofImage& image) {
+ if(type != image.type) {
+ ofLog(OF_LOG_ERROR, "Image type does not match LUT type.");
+ return;
+ }
+ int n = (int) image.getWidth() * (int) image.getHeight();
+ unsigned char* pixels = image.getPixels();
+ if(type == OF_IMAGE_COLOR) {
+ unsigned char* end = &pixels[n * 3];
+ while(pixels != end) {
+ *(pixels++) = rgbGamma[*pixels][0];
+ *(pixels++) = rgbGamma[*pixels][1];
+ *(pixels++) = rgbGamma[*pixels][2];
+ }
+ } else if(type == OF_IMAGE_GRAYSCALE) {
+ unsigned char* end = &pixels[n];
+ while(pixels != end)
+ *(pixels++) = grayGamma[*pixels];
+ }
+}
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <fstream>
+#include "ofMain.h"
+
+class LutFilter {
+public:
+ LutFilter();
+ void setup(string filename);
+ void filter(ofImage& image);
+
+private:
+ static const int levels = 256;
+
+ int type;
+ unsigned char grayGamma[levels];
+ unsigned char rgbGamma[levels][3];
+
+ static void tokenize(const string& str, vector<string>& tokens, const string& delimiters);
+ static int toInt(string str);
+};
Oops, something went wrong.

0 comments on commit 314da73

Please sign in to comment.