最近在做票據識別的編碼工作時遇到一些問題,就是票據上往往會有一些紅色印章把一些重要信息區域給覆蓋了,比如一些開發票人員蓋印章時比較隨意,容易吧一些關鍵區域給遮蔽了,這讓接下來的票據識別很困難,因此,我們必須先對票據圖像進行一定的預處理來移除印章干擾,再進行字符識別,這樣子識別准確率才有保證。
我們從簡單例子說起,比如我們有以下一張票據,上面蓋有紅色印章,雖然該印章沒有遮擋關鍵信息,但是我們還是打算將其移除,那該怎么辦?首先想到的肯定移除紅色像素點的方法,這種方法需要查到紅色的顏色范圍,然后遍歷全圖像素點,在范圍內的像素點就將它設置為白色。這種方法用起來其實不太好,畢竟這個“紅色范圍”的設定還是蠻困難的一件事。那現在我說一下我的方法,用幾行代碼移除紅色印章。
原圖
灰度化
二值化
做票據識別一般都要將票據轉化為二值圖像,我們從上面的二值圖像可以看出,票據上還是存在大塊的印章痕跡,我們此刻的任務就是,將它從票據中移除!
其實實現的方法非常簡單,關鍵就是分離顏色通道 + 閾值分割。步驟如下:
- 對彩色圖分離通道,拿到紅色通道圖
- 進行閾值分割
先看一下用split函數分離出來的三通道圖像
紅色通道
綠色通道
藍色通道
從上面各通道的圖像看出,每個通道的圖像是略有不同,不同的地方就在於對不同顏色的敏感度不同。看一下紅色通道的圖,我們發現原圖中的紅色基本不見了!總結一下就是,原圖中顏色越接近紅色的地方在紅色通道越接近白色。在純紅的地方在紅色通道會出現純白。綠色、藍色也是同樣的道理。
但是仔細觀察一下票據圖像中還是有一些印章痕跡,這時再使用一下閾值分割技術就可以移除一些印章痕跡了。
上面就是閾值分割后的圖,可以看出,該二值圖像已經完全看不出有印章的痕跡了,這時我們可以說比較好地移除了印章干擾。
代碼
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
int main()
{
Mat src = imread("100.bmp");
//resize(src, src, Size(700, 500));
Mat gray;
cvtColor(src, gray, CV_RGB2GRAY);
if (src.empty())
{
printf("fail to open image!\n");
return -1;
}
// 全局二值化
int th = 180; //閾值要根據實際情況調整
Mat binary;
threshold(gray, binary, th, 255, CV_THRESH_BINARY);
vector<Mat> channels;
split(src, channels);
Mat red = channels[2];
Mat blue = channels[0];
Mat green = channels[1];
Mat red_binary;
threshold(red, red_binary, th, 255, CV_THRESH_BINARY);
imshow("src", src);
imshow("gray", gray);
imshow("binary", binary);
imshow("red channel", red);
imshow("blue channel", blue);
imshow("green channel", green);
imshow("red+binary", red_binary);
waitKey();
return 0;
}
來多幾張發票看看效果
移除前
移除后
移除前
移除后
下面這個情形比較經典,因為印章剛好把一些關鍵區域(金額)給遮擋住了,現在人的肉眼也很難辨別出它的具體數字了,那機器還能正確識別嗎?如果不做任何處理,機器也是沒辦法識別的,但是預處理一下之后,機器就能准確識別出其數字了。
移除前
移除后
當然,這種分離通道+閾值分割的方法還可以用到其他場合,例如在紅綠燈的檢測上,也是可以借鑒這種方法的。我在網上找了張紅綠燈的照片來測試,也看看效果吧~
檢測紅燈
檢測綠燈