发票二维码扫描增强_03_图像预处理_01_图像切片
預(yù)處理內(nèi)容介紹
我們?cè)谡嬲膶?duì)二維碼圖形進(jìn)行分割解碼之前,需要將圖形轉(zhuǎn)換成我們需求的形態(tài):
1.只關(guān)注二維碼部分
2.排除掉其他顏色的干擾信息
3.圖片轉(zhuǎn)換成完整的正方形
二維碼切分
從紙質(zhì)發(fā)票的實(shí)際情況來(lái)看,所有的發(fā)票的二維碼部分都是藍(lán)色的。顏色與針式打印機(jī)沒(méi)有太大關(guān)系,國(guó)稅的專票和普票的第一聯(lián)是采用的壓敏紙,針式打印機(jī)的針頭落下的時(shí)候壓敏紙背面的顏色會(huì)印記到第二聯(lián)和第三聯(lián)上,而所有的發(fā)票紙張都是國(guó)稅監(jiān)制的,所以基本上初始打印的顏色是沒(méi)有太大差異的。只可能因?yàn)獒橆^的力度不足而發(fā)生部分欠色,以及由于長(zhǎng)時(shí)間的不妥善保管導(dǎo)致的褪色。
在不考慮特殊情況的前提下,我們目前需要找出當(dāng)前圖片中的一個(gè)藍(lán)色正方形,且排除掉頁(yè)面上的其他藍(lán)色信息。
顏色空間
參考《RGB、YUV和HSV顏色空間模型》:https://www.cnblogs.com/justkong/p/6570914.html
顏色通常用三個(gè)獨(dú)立的屬性來(lái)描述,三個(gè)獨(dú)立變量綜合作用,自然就構(gòu)成一個(gè)空間坐標(biāo),這就是顏色空間。
顏色空間按照基本機(jī)構(gòu)可以分為兩大類:基色顏色空間和色、亮分離顏色空間。前者典型的是RGB,后者典型的是HSV。
在RGB顏色空間中,任意色光F都可以用R(Red 紅色)、G(Green 綠色)、B(Blue 藍(lán)色)三色不同分量的相加混合而成,RGB色彩空間采用物理三基色表示,因而物理意義很清楚,適合彩色顯象管工作。然而這一體制并不適應(yīng)人的視覺(jué)特點(diǎn)。所以無(wú)法用RGB色彩空間很好的描述人肉眼中的藍(lán)色這個(gè)顏色在不同光照下的展現(xiàn)結(jié)果
HSV是一種將RGB色彩空間中的點(diǎn)在倒圓錐體中的表示方法。HSV即色相(Hue)、飽和度(Saturation)、明度(Value),又稱HSB(B即Brightness)。色相是色彩的基本屬性,就是平常說(shuō)的顏色的名稱,如紅色、黃色等。飽和度(S)是指色彩的純度,越高色彩越純,低則逐漸變灰,取0-100%的數(shù)值。明度(V),取0-max(計(jì)算機(jī)中HSV取值范圍和存儲(chǔ)的長(zhǎng)度有關(guān))。
HSV顏色空間,更類似于人類感覺(jué)顏色的方式,封裝了關(guān)于顏色的信息:“這是什么顏色?深淺如何?明暗如何?”
GRAY顏色空間只有一個(gè)灰度的顏色通道
多個(gè)顏色空間可以互相轉(zhuǎn)換
二值化
圖像的二值化是將圖像上的像素點(diǎn)的灰度值設(shè)置為0或255,也就是將整個(gè)圖像呈現(xiàn)出明顯的黑白效果。圖片二值化的結(jié)果可以作為掩模使用。
掩模
參考《OpenCV探索之路(十三):詳解掩膜mask》:https://www.cnblogs.com/skyfsm/p/6894685.html
OpenCV中有一個(gè)概念:ROI(感興趣區(qū)域:Region of interest),但是只支持少量規(guī)則圖形,例如矩形。我們需要對(duì)發(fā)票上的不規(guī)則區(qū)域進(jìn)行裁剪,需要借助掩模(mask)的力量。他可以屏蔽圖片上無(wú)關(guān)區(qū)域,只凸顯出感興趣區(qū)域。簡(jiǎn)單點(diǎn),就是可以用來(lái)?yè)笀D。
按位與
在opencv中,圖像其實(shí)是一個(gè)矩陣,當(dāng)為處于RGB顏色空間時(shí),是一個(gè)三維矩陣,矩陣的維度分別為:[圖片的高度,圖片的寬度,顏色通道的數(shù)量]
當(dāng)使用掩模進(jìn)行摳圖操作時(shí),調(diào)用的是opencv的bitwise_and(按位與)函數(shù),實(shí)際上就是將某個(gè)點(diǎn)的圖像各通道的二進(jìn)制值與相同點(diǎn)位的二進(jìn)制值進(jìn)行了按位與操作
例如,某個(gè)點(diǎn)的顏色信息為(120,155,100)在掩模上是一個(gè)黑色的點(diǎn)(0,0,0),那么執(zhí)行完按位與操作后
120 -> 0111 1000 & 0000 0000 => 0000 0000 155 -> 1001 1011 & 0000 0000 => 0000 0000 100 -> 0110 0100 & 0000 0000 => 0000 0000這個(gè)點(diǎn)在執(zhí)行完按位與操作后完全變成了黑色
同樣,某個(gè)點(diǎn)與掩模上的白色點(diǎn)(255,255,255)執(zhí)行完按位與操作后,仍然保留了原來(lái)的顏色
120 -> 0111 1000 & 1111 1111 => 0111 1000 155 -> 1001 1011 & 1111 1111 => 1001 1011 100 -> 0110 0100 & 1111 1111 => 0110 0100輪廓
輪廓可以簡(jiǎn)單認(rèn)為成將連續(xù)的點(diǎn)(連著邊界)連在一起的曲線,具有相同 的顏色或者灰度。輪廓在形狀分析和物體的檢測(cè)和識(shí)別中很有用。
- 為了更加準(zhǔn)確,要使用二值化圖像。在尋找輪廓之前,要進(jìn)行閾值化處理 或者 Canny 邊界檢測(cè)。
- 查找輪廓的函數(shù)會(huì)修改原始圖像。如果你在找到輪廓之后還想使用原始圖 像的話,你應(yīng)該將原始圖像存儲(chǔ)到其他變量中。
- 在 OpenCV 中,查找輪廓就像在黑色背景中超白色物體。你應(yīng)該記住, 要找的物體應(yīng)該是白色而背景應(yīng)該是黑色。
形態(tài)學(xué)操作
參考《形態(tài)學(xué)圖像處理(一):膨脹與腐蝕》:https://blog.csdn.net/poem_qianmo/article/details/23710721
參考《形態(tài)學(xué)圖像處理(二):開(kāi)運(yùn)算、閉運(yùn)算、形態(tài)學(xué)梯度、頂帽、黑帽合輯》:https://blog.csdn.net/poem_qianmo/article/details/23710721
參考《OpenCV官方教程中文版(For Python)pdf版》
形態(tài)學(xué)操作前提:黑色背景,白色物體
膨脹:白色物體膨脹邊緣侵占黑色背景
----->
腐蝕:黑色背景膨脹邊緣侵占白色物體
----->
開(kāi)運(yùn)算:去除黑色背景上的白色噪點(diǎn)
閉運(yùn)算:去除白色物體內(nèi)的黑色小洞
獲取藍(lán)色區(qū)域的代碼實(shí)現(xiàn)
我們將圖片轉(zhuǎn)換到HSV空間后,通過(guò)設(shè)置顏色的上下限來(lái)過(guò)濾圖片中的藍(lán)色顏色區(qū)域:
參考《【OpenCV】HSV顏色識(shí)別-HSV基本顏色分量范圍》:https://blog.csdn.net/taily_duan/article/details/51506776
# HSV顏色空間下的藍(lán)色上下限lower_blue = np.array([80, 43, 46])upper_blue = np.array([130, 255, 255])# 兩個(gè)問(wèn)題,一個(gè)是蒙版范圍,一個(gè)是膨脹的卷積核的大小# 將BGR顏色空間的圖片轉(zhuǎn)換為HSV顏色空間hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)獲取到的藍(lán)色圖像的二值化圖像為:
通過(guò)按位與操作將圖片中的有效信息摳出的圖像為:
# 使用蒙版將原圖中的區(qū)域摳出part_blue = cv2.bitwise_and(image, image, mask=mask_blue)將獲取到的圖像轉(zhuǎn)換為灰度圖:
# 將BGR顏色空間轉(zhuǎn)換到灰度顏色空間gray_image = cv2.cvtColor(part_blue, cv2.COLOR_BGR2GRAY)將獲取到的灰度圖進(jìn)行二值化處理
(T, thresh_image) = cv2.threshold(gray_image, 55, 255, cv2.THRESH_BINARY)可以看到,二值化之后的圖像其實(shí)并非連續(xù)的,二維碼部分的圖形處于一個(gè)分離狀態(tài)。從輪廓的定義來(lái)看,必須是連續(xù)的圖像才能算作一個(gè)輪廓。我們需要對(duì)整體圖片進(jìn)行一次閉運(yùn)算,消除掉白色物體之間的黑色縫隙,使之連續(xù)
#卷積核大小需要根據(jù)圖像本身的尺寸和圖片上的相鄰圖形的間隔進(jìn)行調(diào)整kernel = np.ones((30, 30), np.uint8)close_image = cv2.morphologyEx(thresh_image, cv2.MORPH_CLOSE, kernel)從結(jié)果來(lái)看,圖片上存在多個(gè)白色物體,需要對(duì)其進(jìn)行篩選。從物體的形狀有多種篩選的依據(jù):
1.白色圖形面積最大
2.白色圖形的輪廓對(duì)應(yīng)的長(zhǎng)寬比接近1
因?yàn)檩喞旧硎且淮B續(xù)的點(diǎn),沒(méi)有真正的長(zhǎng)寬意義,需要重新繪制出最小外接矩形,再來(lái)計(jì)算外接矩形的長(zhǎng)寬比,邏輯比較復(fù)雜,所以我選擇了通過(guò)面積計(jì)算來(lái)過(guò)濾
# 尋找輪廓close_image, contours, hierarchy = cv2.findContours(close_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)# 刪選輪廓# 當(dāng)前圖片中面積最大的輪廓即為二維碼max_area = 0max_index = 0for i in range(0, len(contours)):area = cv2.contourArea(contours[i]) # 外框面積 #外界矩陣的面積if area > max_area:max_index = imax_area = areaif len(contours) == 0:return Noneqr_cnt = contours[max_index]將尋找到的合適的輪廓進(jìn)行描邊,有兩種描邊方案
1.輪廓本身有一個(gè)drawContours的方法
2.對(duì)輪廓進(jìn)行多邊形擬合,然后對(duì)擬合的多邊形進(jìn)行繪圖
我選擇的是多邊形擬合再繪圖的方案(具體原因后續(xù)講)
對(duì)這張黑色背景的圖片取反,獲得對(duì)應(yīng)的白色圖片
bit_not_poly_image = cv2.bitwise_not(poly_image)將白色圖片的黑色線框中填充黑色線段,由于opencv自帶的多邊形填充fillPoly在遇到內(nèi)凹圖形的時(shí)候會(huì)導(dǎo)致填充斷線,所以寫了一段遞歸的邏輯,不斷尋找最小的輪廓面積進(jìn)行填充,直到輪廓數(shù)量<=2
fill_image = self.fill_black_poly(bit_not_poly_image) # 遞歸填充黑色多邊形def fill_black_poly(self, poly_image):# 傳入黑色多邊形# 如果當(dāng)前多邊形找到的輪廓數(shù)量小于等于2,說(shuō)明當(dāng)前圖形已經(jīng)被完全填充,無(wú)需再進(jìn)行填充# 否則說(shuō)明當(dāng)前多邊形還存在空白區(qū)域,需要繼續(xù)填充poly_image, contours, hierarchy = cv2.findContours(poly_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)if len(contours) <= 2:return poly_imageelse:minArea = -1;# 從輪廓中找到面積最小的輪廓進(jìn)行填充for i in range(0, len(contours)):if cv2.contourArea(contours[i]) < minArea or minArea == -1:minArea = cv2.contourArea(contours[i])minIndex = icv2.fillConvexPoly(poly_image, contours[minIndex], (0, 0, 0), 8, 0)if self.trace_image:cv2.imwrite(self.trace_path + "008_fill_poly_" + str(minArea) + self.image_name, poly_image)return self.fill_black_poly(poly_image)將填充完成的圖片按位取反,并進(jìn)行二值化處理,作為截取二維碼圖片的蒙版,并執(zhí)行圖片截取
# 將填充完成的圖片按位取反并進(jìn)行二值化處理,作為截取二維碼區(qū)域的蒙版qr_mask_image = cv2.bitwise_not(fill_image)ret, qr_mask = cv2.threshold(qr_mask_image, 175, 255, cv2.THRESH_BINARY)# 通過(guò)蒙版截取圖片二維碼部分cut_image = cv2.bitwise_and(image, image, mask=qr_mask)截取圖片后,將圖片的全黑部分修改為全白,避免由于圖片黑色過(guò)多導(dǎo)致后續(xù)二值化處理異常
# 將黑色部分修改為白色,方便后續(xù)處理cut_image = np.where(cut_image == 0, 255, cut_image)轉(zhuǎn)載于:https://www.cnblogs.com/mousezhou/p/9256627.html
總結(jié)
以上是生活随笔為你收集整理的发票二维码扫描增强_03_图像预处理_01_图像切片的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux gcc/g++链接编译顺序详
- 下一篇: 【洛谷P4124】[CQOI2016]手