Buy us Beer
Categories
Pages

Recoloring via Histogram Matching with OpenCV [w/ code]

Hi

I wanted to do the simplest recoloring/color-transfer I could find – and the internet is just a bust. Nothing free, good and usable available online… So I implemented the simplest color transfer algorithm in the wolrd – Histogram Matching.

Here’s the implementation with OpenCV

// Compute histogram and CDF for an image with mask
void do1ChnHist(const Mat& _i, const Mat& mask, double* h, double* cdf) {
Mat _t = _i.reshape(1,1);
Mat _tm;
mask.copyTo(_tm);
_tm = _tm.reshape(1,1);
for(int p=0;p<_t.cols;p++) {
if(_tm.at(0,p) > 0) {
uchar c = _t.at(0,p);
h[c] += 1.0;
}
}

//normalize hist
Mat _tmp(1,256,CV_64FC1,h);
double minVal,maxVal;
minMaxLoc(_tmp,&minVal,&maxVal);
_tmp = _tmp / maxVal;

cdf[0] = h[0];
for(int j=1;j<256;j++) {
cdf[j] = cdf[j-1]+h[j];
}

//normalize CDF
_tmp.data = (uchar*)cdf;
minMaxLoc(_tmp,&minVal,&maxVal);
_tmp = _tmp / maxVal;
}

// match histograms of 'src' to that of 'dst', according to both masks
void histMatchRGB(Mat& src, const Mat& src_mask, const Mat& dst, const Mat& dst_mask) {
#ifdef BTM_DEBUG
	namedWindow("original source",CV_WINDOW_AUTOSIZE);
	imshow("original source",src);
	namedWindow("original query",CV_WINDOW_AUTOSIZE);
	imshow("original query",dst);
#endif

	vector<Mat> chns;
	split(src,chns);
	vector<Mat> chns1;
	split(dst,chns1);
	Mat src_hist = Mat::zeros(1,256,CV_64FC1);
	Mat dst_hist = Mat::zeros(1,256,CV_64FC1);
	Mat src_cdf = Mat::zeros(1,256,CV_64FC1);
	Mat dst_cdf = Mat::zeros(1,256,CV_64FC1);
	Mat Mv(1,256,CV_8UC1);
	uchar* M = Mv.ptr<uchar>();

	for(int i=0;i<3;i++) {
		src_hist.setTo(cvScalar(0));
		dst_hist.setTo(cvScalar(0));
		src_cdf.setTo(cvScalar(0));
		src_cdf.setTo(cvScalar(0));

		do1ChnHist(chns[i],src_mask,src_hist,src_cdf);
		do1ChnHist(chns1[i],dst_mask,dst_hist,dst_cdf);

		uchar last = 0;
		double* _src_cdf = src_cdf.ptr<double>();
		double* _dst_cdf = dst_cdf.ptr<double>();

		for(int j=0;j<src_cdf.cols;j++) {
			double F1j = _src_cdf[j];

			for(uchar k = last; k<dst_cdf.cols; k++) {
				double F2k = _dst_cdf[k];
				if(abs(F2k - F1j) < HISTMATCH_EPSILON || F2k > F1j) {
					M[j] = k;
					last = k;
					break;
				}
			}
		}

		Mat lut(1,256,CV_8UC1,M);
		LUT(chns[i],lut,chns[i]);
	}

	Mat res;
	merge(chns,res);

#ifdef BTM_DEBUG
	namedWindow("matched",CV_WINDOW_AUTOSIZE);
	imshow("matched",res);

	waitKey(BTM_WAIT_TIME);
#endif

	res.copyTo(src);
}

Edit 31/1/10: nicer code in histMatch, better memory consumption.

The code is fairly simple.. I started using the OpenCV 2.0 C++ API and I must say it’s great, not having to worry about silly memory managment, everything is instanced on the stack and get’s released automatically.

Just a quick overview:

  • Compute histogram and comulative distibution function (CDF) for each image.
  • Create the lookup table (LUT) from one CDF to the other
  • Apply the LUT to the image

External usage is simple

Mat src=cvLoadImage("image008.jpg");
Mat dst=cvLoadImage("image003.jpg");
Mat src_mask = cvLoadImage("image008_mask.bmp",0);
Mat dst_mask = cvLoadImage("image003_mask.bmp",0);

histMatchRGB(dst,dst_mask,src,src_mask);

Enjoy!
Roy.

  • Share/Bookmark

6 Responses to “Recoloring via Histogram Matching with OpenCV [w/ code]”

  • Zac:

    What value are you using for HISTMATCH_EPSILON?

  • Roy:

    #define HISTMATCH_EPSILON 0.000001

  • Zac:

    Thanks very much Roy, I am currently looking at modifying your code to work with 16 bit images.

    Zac

  • Thanks a lot for your effort! there’s a couple of hiccups that needed to be fixed (at least for me) for the code to run but it does, and very smoothly! Very nice and elegant! (and it finally got me to switch to proper cv2.0…)

    Much appreciated!

  • Roy:

    Hi Basilio,
    Thanks
    Just saw your online portfolio – some awesome stuff you got there!
    R.

  • Andrea:

    Hi, this code is great, i’m using it but i have some trouble with some artifacts, when there is a great chromatic difference betwen the images some strange halo, with wrong colors, appears :(

    How i can solve it?

Leave a Reply