Toriyung

导航

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过小也会产生同样的情况(拟合结果边数多)。

 

posted on 2022-03-15 21:56  Toriyung  阅读(33)  评论(0编辑  收藏  举报