初学者的领悟|百分比截断方法增加图像对比度的原理

本人亲自使用opencv中的直方图均衡化处理了三波段的jpg,但是效果很差,对比度使增强了,就是颜色很怪异,且使用了HSV和HIS和YCrBr等颜色空间也不行,于是自己研究了一下百分比截断原理,自己写了一个百分比截断函数,搞定了,如果想要代码:加qq1577537753,顺便点一波关注。
由于各种原因,图像的像素值很可能会聚集在某一个区间范围内,例如:
一幅影像位深为8bits,其值范围理论为[0,255],但是实际的像元值范围在[30-50]之间,在我们拿照片查看器或者其他不会进行影像自动拉伸的软件查看该图像时,该图像在视觉上会显示为一片白或者发暗,这并不是说影像是不能使用的,或者坏死的,有可能只是影像的对比度不够导致视觉上影响出现了问题,这时就可以采用百分比截断的思想来进行图像处理增加图像的对比度。
【初学者的领悟|百分比截断方法增加图像对比度的原理】百分比截断原理:
如上,影像的像元值实际范围在[30,50]之间,也就是说影像的实际像元值在[0-30]和[50-255]之间的像元个数很少,可能连影像总个数的4%都不到。这时就涉及到两个百分比,第一个是下限百分比:[0-30]的像元个数占影像总像元个数的x1%;第二个是上限百分比:[50-255]的像元个数占影像总像元个数的x2%。此时所谓的百分比截断就是将值在[0-30]范围内的像元值修改为0,将值[30-50]范围内的像元值修改为255.这就是截断。
剩下的就是拉伸了,对于[30-50]内的像元值,我们要让其重新分布,将其修改到[0-255],这样就是为了增大像元值的相对分布距离,也就是增加对比度,使的一些软件可以不自动拉伸也可以让人看到正常的影像。
怎样才能使[30-50]范围修改为[0-255]呢?我们这样思考,原来[30-50]的区间宽度20要修改为[0-255]的区间宽度255,即20要变为255,相当于原来25米长的橡皮绳要拉长到255米,也就是原来的1米代表了新的255/20=12.75米。而这个比例12.75仅适用于在[30-50]范围内的像元。
此处示例一下:
示例一:一像元值为20,那其拉伸后的值为0,因为不在[30-50]之内
示例二:一像元值为198,那其拉伸后的值为0,因为不在[30-50]之内
示例二:一像元值为45,那么怎样计算拉伸后的值呢,(45-30)×12.75=191.25,有人会说这样计算对不对呢?
验证一下:一像元值为30,按照我们要提高对比度的想法,其拉伸后的理论值为0,;一像元值为50,按照我们的想法,其拉伸后的理论值为255.如下:
(30-30)×12.75=0(50-30)×12.75=255
很明显是正确的。
其实以上示例已经分情况说明了百分比截断的分段函数,而一般的图像处理上下限截断百分比各为2%。
以下为本人按照截断百分比2%来获取4个波段各自的有效上下限的计算过程,也就是[30-50]是如何得到的。其中用到的获取直方图的函数gdal里面的,你也可以使用opencv或者其他库函数进行直方图获取,原理一致。

double minPbuf1 = 0, maxPbuf1 = 0; double minPbuf2 = 0, maxPbuf2 = 0; double minPbuf3 = 0, maxPbuf3 = 0; double minPbuf4 = 0, maxPbuf4 = 0; for (int r = 1; r <= nBands; ++r) { GDALRasterBand*xband = ImgBef->GetRasterBand(r); int panHistogram[256] = { 0 }; xband->GetHistogram(0, 255.5, 256, panHistogram, TRUE, FALSE, NULL, NULL); double minNum = 0, minPbuf = 0; for (int i = 1; i < 256; i++) { minNum = minNum + panHistogram[i]; if (double(minNum / (nCols*nRows)) > 0.02) { minPbuf = i; break; } } double maxNum = 0, maxPbuf = 0; for (int i = 254; i > 0; i--) { maxNum = maxNum + panHistogram[i]; if (double(maxNum / (nCols*nRows)) > 0.02) { maxPbuf = i; break; } }if (r == 1) { minPbuf1 = minPbuf; maxPbuf1 = maxPbuf; } if (r == 2) { minPbuf2 = minPbuf; maxPbuf2 = maxPbuf; } if (r == 3) { minPbuf3 = minPbuf; maxPbuf3 = maxPbuf; } if (r == 4) { minPbuf4 = minPbuf; maxPbuf4 = maxPbuf; } }

获取到4个波段各自的有效上下限后就可以对每个像元的值进行如下处理,也就是分段函数。获取某个像元值的过程就不赘述了,如果你不知道,说明你需要学一下opencv和gdal或者其他库或者某个像元值的部分。以下是分情况处理像元值的部分,也是百分比阶段的核心,本人要处理的是png,所以第四波段值写为了255,使其不透明(具体为什么写为255,自行百度),所以也就不存在分情况讨论,就没有处理了,只处理了3个波段:
if (pbuf[0 + i*nBands] < minPbuf1) { pImg1[0 + i*nBands] = 0; } if (pbuf[0 + i*nBands] > maxPbuf1) { pImg1[0 + i*nBands] = 255; } if (pbuf[0 + i*nBands] >= minPbuf1&&pbuf[0 + i*nBands] <= maxPbuf1) { pImg1[0 + i*nBands] = int(((pbuf[0 + i*nBands] * 1.0) - minPbuf1) * (255/(maxPbuf1 - minPbuf1))); } //2 band if (pbuf[1 + i*nBands] < minPbuf2) { pImg1[1 + i*nBands] = 0; } if (pbuf[1 + i*nBands] > maxPbuf2) { pImg1[1 + i*nBands] = 255; } if (pbuf[1 + i*nBands] >= minPbuf2&&pbuf[1 + i*nBands] <= maxPbuf2) { pImg1[1 + i*nBands] = int(((pbuf[1 + i*nBands] * 1.0) - minPbuf2) * (255 / (maxPbuf2 - minPbuf2))); } //3 band if (pbuf[2 + i*nBands] < minPbuf3) { pImg1[2 + i*nBands] = 0; } if (pbuf[2 + i*nBands] > maxPbuf3) { pImg1[2 + i*nBands] = 255; } if (pbuf[2 + i*nBands] >= minPbuf3&&pbuf[2 + i*nBands] <= maxPbuf3) { pImg1[2 + i*nBands] = int(((pbuf[2 + i*nBands] * 1.0) - minPbuf3) * (255 / (maxPbuf3 - minPbuf3))); }

最后将处理完的像元值写到新的图像或者覆盖掉原来的图像都可以,处理完后就是对比度增强后的图了。左边为结果图,右边为原图。


    推荐阅读