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 += 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.