Opencv Q&A_8
2022/03/14 - 2022/03/15
对电子文档图片高亮部分裁剪并识别
此次project使用的功能函数另起python文件存放名为utlis.py
代码
(main) highlight_text_detection.py
import cv2 as cv import numpy as np from utlis import utlis import pytesseract as pytess lower = np.array([0,32,212]) upper = np.array([180,255,255]) string = [] img = cv.imread('D:\work\\automation\Skill\Python\pythonWORK\cv\material\\text2.png') pytess.pytesseract.tesseract_cmd = 'D:\work\\automation\Skill\Python\pythonWORK\cv\OCR\Tesseract\\tesseract.exe' img_high = utlis.find_highlight(img,lower,upper) contours = utlis.find_all_contour(img_high,threslist=[50,100],mode='certain') right_contour = utlis.find_right_contour(contours,1000,4,0.01) ### 画出最小边框 img_exs = utlis.draw_bounding(img,right_contour,'bounding') cv.imshow('img_',img) cv.waitKey(0) ### 裁剪出高亮部分单独显示,并使用OCR识别文字写入文件 img_exs = utlis.draw_bounding(img,right_contour,'extract') with open('D:\work\\automation\Skill\Python\pythonWORK\cv\highlight_text_detection\\text_detection_output.txt','w') as f: for n,img_ex in enumerate(img_exs): strout = pytess.image_to_string(img_ex,lang='chi_sim') print(strout) f.write(strout) cv.imshow('img_' + str(n), img_ex) cv.waitKey(0)
utlis.py
import cv2 as cv import numpy as np def nothing(x): ### 滑条功能函数 pass def color_picker(img): ### 通过滑条进行实时选择阈值 cv.namedWindow('color_picker') cv.createTrackbar('lower_h', 'color_picker', 0, 180,nothing) cv.createTrackbar('lower_s', 'color_picker', 0, 255,nothing) cv.createTrackbar('lower_v', 'color_picker', 0, 255,nothing) cv.createTrackbar('upper_h', 'color_picker', 0, 180,nothing) cv.createTrackbar('upper_s', 'color_picker', 0, 255,nothing) cv.createTrackbar('upper_v', 'color_picker', 0, 255,nothing) while True: img_copy = img.copy() #保证原图img不会被覆盖,每一次操作都用img的副本img_copy lh = cv.getTrackbarPos('lower_h', 'color_picker') ls = cv.getTrackbarPos('lower_s', 'color_picker') lv = cv.getTrackbarPos('lower_v', 'color_picker') uh = cv.getTrackbarPos('upper_h', 'color_picker') us = cv.getTrackbarPos('upper_s', 'color_picker') uv = cv.getTrackbarPos('upper_v', 'color_picker') if lh < uh and ls < us and lv < uv: lower = np.array([lh,ls,lv]) upper = np.array([uh,us,uv]) img_copy = find_highlight(img_copy,lower,upper) print(lower) print(upper) cv.imshow('color_picker',img_copy) cv.waitKey(1) def find_highlight(img,lower,upper): ### 找出图片高亮区域 img = cv.cvtColor(img,cv.COLOR_BGR2HSV) mask = cv.inRange(img,lower,upper) #双阈值掩膜,在阈值范围外全为黑色(0,0,0),阈值范围内为白色(255,255,255) img_highlight = cv.bitwise_and(img,img,mask=mask) #位与操作截出阈值内(高亮)区域 return img_highlight def find_all_contour(img,threslist,mode='test'): ### 找出所有边缘 if mode == 'test': #canny阈值不确定时进行实时选择 cv.namedWindow('color_picker') cv.createTrackbar('lower_h', 'color_picker', 0, 255, nothing) cv.createTrackbar('lower_s', 'color_picker', 50, 255, nothing) while True: lh = cv.getTrackbarPos('lower_h', 'color_picker') ls = cv.getTrackbarPos('lower_s', 'color_picker') img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) img_blur = cv.GaussianBlur(img_gray, (3, 3), 0) img_canny = cv.Canny(img_blur, lh, ls) cv.imshow('color_picker', img_canny) cv.waitKey(1) if mode == 'certain': #canny阈值确定时使用 img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) img_blur = cv.GaussianBlur(img_gray, (3, 3), 0) img_canny = cv.Canny(img_blur, threshold1=threslist[0], threshold2=threslist[1]) img_close = cv.morphologyEx(img_canny,cv.MORPH_CLOSE,(3,3)) img_contour,hierarchy = cv.findContours(img_close,cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) return img_contour def find_biggest_contour(contours,minArea): ### 得到最大边框 Max_area = 0 biggest_contour = [] for contour in contours: area = cv.contourArea(contour) length = cv.arcLength(contour,True) lines = cv.approxPolyDP(contour,length*0.02,True) if area > minArea and area > Max_area and len(lines) == 4: Max_area = area biggest_contour = lines return biggest_contour def find_right_contour(contours,minArea,filter=4,accuracy=0.01): ### 得到满足条件的曲线近似多边形框和最小矩形框合集 biggest_contour = [] for contour in contours: area = cv.contourArea(contour) length = cv.arcLength(contour,True) lines = cv.approxPolyDP(contour,length*accuracy,True) #accuracy太小 rect = cv.boundingRect(lines) if area > minArea and len(lines) == filter: biggest_contour.append([lines,rect]) #合成列表输出 return biggest_contour def reorder(locations,mod): ## 重新编排坐标信息顺序 lists = [] list = [] for location in locations: if mod == 'perspective': # 透视使用:↖↙↗↘ list.append(location[0][0]) list.append(location[1][0]) list.append(location[3][0]) list.append(location[2][0]) if mod == 'polylines': # 多边形使用:↖↗↘↙ list.append(location[0][0]) list.append(location[3][0]) list.append(location[2][0]) list.append(location[1][0]) list = np.array(list, np.int32) lists.append(list) return lists def draw_bounding(img,rects,mode): ### 画最小边框,或单独抽出框内图像 if mode == 'bounding': #画最小边框 for rect in rects: x, y, w, h = rect[1] #0为曲线近似边框,1位最小矩形边框 cv.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2) if mode == 'extract': #抽出框内图像返回图像合集 img_list = [] for rect in rects: x, y, w, h = rect[1] # 0为曲线近似边框,1位最小矩形边框 img_extract = img[y:y+h,x:x+w] img_list.append(img_extract) return img_list
运行效果
图一 原图
图二 框选出高亮区域
图三 单独截取出高亮区域并识别
遇到的问题
Q1:cv.inrange()解析
A1:双阈值掩膜,结合cv.bitwise_and等使用截取特定区域。在阈值范围外全为黑色(0,0,0),阈值范围内为白色(255,255,255)
Q2:cv.approxPolyDP()中epsilon过大导致得不到特定边框
A2:cv.approxPolyDP是对曲线进行多边形化,epsilon越小即精度越高拟合的多边形边数越多,如果过大则边数可能会变少,则得不到需要的边数的边框(拟合结果边数少)。同理,在某种情况下(如曲线自身没有明显多边形倾向)epsilon过小也会产生同样的情况(拟合结果边数多)。