基于opencv跟c++的图像处理:直方图匹配
基于opencv和c++的图像处理:直方图匹配
完整代码可参考这里
在冈萨雷斯的那本《数字图像处理》中提到了一种神奇的变换:直方图匹配变换(Histogram Matching), 输入两幅图A和B,A和B的直方图不同,直方图匹配变换是这样的一个变换s = F(r), 使得变换之后,A的直方图和B的直方图一样。也就是它们的颜色分布变成一样。
比如下面两幅图:
一个沙漠,一个海滩,它们的RGB直方图显然是不一样的。
但是执行直方图匹配变换后,沙漠那张图就变成这样了
和海滩那张图的直方图比一下,会发现上图的直方图与之几乎是一样的。沙漠图也就带上了海滩的味道。 这个变换的神奇之处在于,假设海滩那种图中的每个像素点都是可以*移动的,在经过某次神奇的移动之后,海滩变成了沙漠,但是他们视觉上的色彩效果是一致的,不同的是像素点在不同的位置所造成的结构上的差异。
细看之后,海滩化后的沙漠怎么还有一个地方是沙子的颜色呢?其实是海滩上的沙子移过来的。当然,这个变换其实是近似的,主要是因为其中用到了一个变换的反变换,而该变换并不是双射,所以其反变换是近似的,这个也是代码中比较复杂的地方。详细的算法细节可以参考冈萨雷斯的那本数字图像处理,当然最好是看英文版,那本中文版我是根本没看懂。
相关代码:
bool GFImage::HistogramMatching(const GFImage& anoImage) { assert(GetChannel() == anoImage.GetChannel()); //r->s vector<vector<uchar> > vRSMap; CalculateMapFunByHisEq(vRSMap); //z->s vector<vector<uchar> > vZSMap; anoImage.CalculateMapFunByHisEq(vZSMap); vector<vector<uchar> > vRZMap; vRZMap.resize(GetChannel()); for(int ch = 0; ch < GetChannel(); ch++) { vRZMap[ch].resize(256); } for (int ch = 0; ch < GetChannel(); ch++) { vector<int> vSZMap; vSZMap.resize(256); for (int i = 0;i < 256;i++) { vSZMap[i] = -1; } for (int z = 255; z>=0; z--) { vSZMap[vZSMap[ch][z]] = z; } vector<int> vSIndex; //加前哨 vSIndex.push_back(-1); for (int s = 0; s< 256 ;s++) { if (vSZMap[s] != -1) { vSIndex.push_back(s); } } //加后哨 vSIndex.push_back(256); for (int i = 0; i < vSIndex.size() - 1; i++) { int startIdx = vSIndex[i] + 1; int endIdx = vSIndex[i + 1] - 1; int lowerIdx = vSIndex[i]; int upperIdx = vSIndex[i + 1]; if (i == 0) { lowerIdx = upperIdx; } if ( i == vSIndex.size() - 2) { upperIdx = lowerIdx; } int nLen = endIdx - startIdx + 1; int nMidIdx = startIdx + nLen / 2; for (int j = startIdx; j < nMidIdx; j++) { vSZMap[j] = vSZMap[lowerIdx]; } for (int j = nMidIdx; j <= endIdx ; j++) { vSZMap[j] = vSZMap[upperIdx]; } } for (int r = 0; r < 256; r++) { vRZMap[ch][r] = vSZMap[vRSMap[ch][r]]; } } for (int ch = 0; ch < GetChannel(); ch++) { uchar * pData = GetData(); for (int r = 0;r < GetHeight(); r++) { uchar * pLine = pData + r * GetWidthStep(); for (int c = 0; c < GetWidth(); c++) { uchar val = pLine[GetChannel() * c + ch]; pLine[GetChannel() * c + ch] = vRZMap[ch][val]; } } } return true; }
完整代码可参考这里