因爲項目需要對文檔圖像進行增強,也就是對於模糊、亮度偏暗或不均勻的文檔進行處理方便後續的識別。傳圖圖像增強方法主要分爲兩方面:空間域和頻域。空間閾中增強方法,顏色的增強,如:直方圖均衡化,對比度以及gama增強等;模糊,如:均值濾波等;銳化,如:局部標準差實現對比度增強。頻域方法,如:小波變換,在圖像的某個變換域內,對圖像的變換系數進行運算,然後通過逆變換獲得圖像增強效果。一般來說,對於實際項目中,可能用其中一種或幾種方法來進行圖像增強效果一般會很差,我們更多的是對這些算法進行融合,來達到我們想要的效果。
1、基於劃分模式的圖像增強
假設我們有圖像A、B,分別爲同一場景在不一樣的光照拍攝圖片,其中A表示基色 ,B表示混合色。那麼我們對該場景下的光照分佈進行建模得到,劃分模式的計算公式:結果色 = (基色 / 混合色) *255。
- 具體什麼意思呢?
我們分析每個通道的數值,並基於基色進行增強,如果基色數值大於或等於混合色,那麼結果色就爲白色;如果基色小於混合色,結果會比基色更暗。
- 這個算法爲什麼比較適合文檔圖像增強?
我們知道文檔圖像一般主要有文字和背景組成。其中文字爲黑色,背景爲白色。如果單純的進行對比度之類的增強,對於較亮或較暗的圖像確實有效,但對於文檔,這種方法反而使得文本部分缺失,反而沒有達到增強的效果。基於劃分模式的圖像增強,正是考慮這點,他對於暗的,如:字體,會變的更暗,對於亮的,如:背景,會變的更亮。
- 具體怎麼實現?
問題規範爲圖像A,B,爲同一場景在不一樣的光照拍攝圖片,那麼:
光照分佈 L = A / B
如果已知 A, L ,則 B = A / L (B 爲A去光照的結果)
這裏,A表示我們需要增強的圖片,B表示經過高斯濾波後的圖片。那麼,我們進行A/B就得到增強後的圖像了。是不是很簡單。
2、基於c++ OpenCV的實現
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat image = imread("./pic/raw.png");
//劃分算法
//如果混合色與基色相同則結果色爲白色
//如混合色爲白色則結果色爲基色不變
//如混合色爲黑色則結果色爲白色
Mat src = image.clone();
src.convertTo(src, CV_32FC3, 1.0 / 255);
Mat gauss;
Mat dst = src.clone();
GaussianBlur(src, gauss, Size(101, 101), 0);
dst = src / gauss;
dst.convertTo(dst, CV_8UC3, 255);
imshow("dst", dst);
waitKey();
return 0;
}
結果展示:
3、輔助增強算法
我們看到部分圖像處理的並不好,如第1、3行,處理後的結果存在大量的黑色噪聲,所以後續可以使用其它增強的算法來輔助進行。這裏我們選擇gamma對比度增強算法。具體實現如下:
//Gamma校正 fGamaa=0.45是常用值
void GammaCorrection(Mat& src, Mat& dst, float fGamma)
{
CV_Assert(src.data);
// accept only char type matrices
CV_Assert(src.depth() != sizeof(uchar));
// build look up table
unsigned char lut[256];
for (int i = 0; i < 256; i++)
{
lut[i] = saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);
}
dst = src.clone();
const int channels = dst.channels();
switch (channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for (it = dst.begin<uchar>(), end = dst.end<uchar>(); it != end; it++)
*it = lut[(*it)];
break;
}
case 3:
{
MatIterator_<Vec3b> it, end;
for (it = dst.begin<Vec3b>(), end = dst.end<Vec3b>(); it != end; it++)
{
(*it)[0] = lut[((*it)[0])];
(*it)[1] = lut[((*it)[1])];
(*it)[2] = lut[((*it)[2])];
}
break;
}
}
}
完整代碼如下:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
//Gamma校正 fGamaa=0.45是常用值
void GammaCorrection(Mat& src, Mat& dst, float fGamma)
{
CV_Assert(src.data);
// accept only char type matrices
CV_Assert(src.depth() != sizeof(uchar));
// build look up table
unsigned char lut[256];
for (int i = 0; i < 256; i++)
{
lut[i] = saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);
}
dst = src.clone();
const int channels = dst.channels();
switch (channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for (it = dst.begin<uchar>(), end = dst.end<uchar>(); it != end; it++)
*it = lut[(*it)];
break;
}
case 3:
{
MatIterator_<Vec3b> it, end;
for (it = dst.begin<Vec3b>(), end = dst.end<Vec3b>(); it != end; it++)
{
(*it)[0] = lut[((*it)[0])];
(*it)[1] = lut[((*it)[1])];
(*it)[2] = lut[((*it)[2])];
}
break;
}
}
}
int main(int argc, char** argv)
{
Mat image = imread("./pic/raw.jpg");
劃分算法
//如果混合色與基色相同則結果色爲白色
//如混合色爲白色則結果色爲基色不變
//如混合色爲黑色則結果色爲白色
Mat src = image.clone();
src.convertTo(src, CV_32FC3, 1.0 / 255);
Mat gauss;
Mat dst = src.clone();
GaussianBlur(src, gauss, Size(101, 101), 0);
dst = src / gauss;
dst.convertTo(dst, CV_8UC3, 255);
gamma變換
Mat matGamma;
GammaCorrection(dst.clone(), matGamma,1.5);
//顯示最終結果
//namedWindow("Soure", 0);
namedWindow("dst", 0);
imshow("Soure", image);
imshow("dst", matGamma);
waitKey();
return 0;
}
參考連接: