转自:http://blog.csdn.net/zhangxb35/article/details/47979939
前面我们已经学了一些OpenCV中基本的图片处理的知识,可以拿来做一些小应用。比如怎样从一张图片中,把文字圈出来。这一步骤对OCR(Optical Character Recognition)非常有用,因为一般的OCR引擎只是拿来识别文字,并没有对图片做预处理,因此精度可能会收到图片质量影响。
当然,我们这里只是粗略的查找文字区域,并没有进一步地处理图片。而且对背景复杂,或者文字旋转角度过于倾斜的情况也无法自适应,因此只能给大家做参考。若要实用到项目中,还有很多工作要做。
效果图
比如我们有下面的一篇文章的截图,想把其中的文字区域全部找出来。
当然这里的截图只有文字和白花花的背景,效果会非常好。绿色的的矩形框的是我们根据检测到的文字区域,手动画出来的。
原理
那么我们是怎么做到检测到区域的呢?
首先,我们会注意到,文字区域和其他的图片背景很不一样。我们用膨胀处理图片,让文字变成一块块大区域,然后识别整块的轮廓,用矩形去框住这个轮廓。
这个程序分三个子函数,detect(检测),preprocess(图片预处理),findTextRegion(查找和筛选文字区域)。即main函数调用detect函数去实际完成文字区域检测。detect函数又分成preprocess和findTextRegion两个步骤来做。
见下面的序列图,可能会清晰点。
mainmaindetectdetectpreprocesspreprocessfindTextRegionfindTextRegion检测文本区域返回检测到的文本矩形Sobel,二值化,膨胀和腐蚀Morphology方法预处理图片返回预处理后的图片轮廓检测,去掉面积小的,过长的查找和筛选文字区域返回区域box的坐标
1. Detect
先来看main函数和Detect函数
def detect(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) dilation = preprocess(gray) region = findTextRegion(dilation) for box in region: cv2.drawContours(img, [box], 0, (0, 255, 0), 2) cv2.namedWindow("img", cv2.WINDOW_NORMAL) cv2.imshow("img", img) cv2.imwrite("contours.png", img) cv2.waitKey(0) cv2.destroyAllWindows()if __name__ == '__main__': imagePath = sys.argv[1] img = cv2.imread(imagePath) detect(img)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
2. Preprocess
利用数学形态学(Morphology)进行预处理。
这个过程很重要,是文字区域检测效果好坏的核心代码,尤其是一下几个参数:
- 膨胀的核函数大小,这里用了
30 x 9
,可以调节 - 腐蚀的核函数大小,这里用了
24 x 6
,可以调节
def preprocess(gray): sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 0, ksize = 3) ret, binary = cv2.threshold(sobel, 0, 255, cv2.THRESH_OTSU+cv2.THRESH_BINARY) element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 9)) element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (24, 6)) dilation = cv2.dilate(binary, element2, iterations = 1) erosion = cv2.erode(dilation, element1, iterations = 1) dilation2 = cv2.dilate(erosion, element2, iterations = 3) cv2.imwrite("binary.png", binary) cv2.imwrite("dilation.png", dilation) cv2.imwrite("erosion.png", erosion) cv2.imwrite("dilation2.png", dilation2) return dilation2
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
3. findTextRegion
def findTextRegion(img): region = [] contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) for i in range(len(contours)): cnt = contours[i] area = cv2.contourArea(cnt) if(area < 1000): continue epsilon = 0.001 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) rect = cv2.minAreaRect(cnt) print "rect is: " print rect box = cv2.cv.BoxPoints(rect) box = np.int0(box) height = abs(box[0][1] - box[2][1]) width = abs(box[0][0] - box[2][0]) if(height > width * 1.2): continue region.append(box) return region
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
完整代码
加上头文件,把几个函数合并以后,贴在这里。注意开头要写明用utf8编码,不然中文注释可能不会被系统识别。而且Python没有花括号来控制流程,所以对看不见的Tab缩进很敏感,写代码的时候要规范。
直接在终端里敲下面的命令,既可以运行
python textDetection.py ./pic/1.png
代码:textDetection.py
import sysimport cv2import numpy as npdef preprocess(gray): sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 0, ksize = 3) ret, binary = cv2.threshold(sobel, 0, 255, cv2.THRESH_OTSU+cv2.THRESH_BINARY) element1 = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 9)) element2 = cv2.getStructuringElement(cv2.MORPH_RECT, (24, 6)) dilation = cv2.dilate(binary, element2, iterations = 1) erosion = cv2.erode(dilation, element1, iterations = 1) dilation2 = cv2.dilate(erosion, element2, iterations = 3) cv2.imwrite("binary.png", binary) cv2.imwrite("dilation.png", dilation) cv2.imwrite("erosion.png", erosion) cv2.imwrite("dilation2.png", dilation2) return dilation2def findTextRegion(img): region = [] contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) for i in range(len(contours)): cnt = contours[i] area = cv2.contourArea(cnt) if(area < 1000): continue epsilon = 0.001 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) rect = cv2.minAreaRect(cnt) print "rect is: " print rect box = cv2.cv.BoxPoints(rect) box = np.int0(box) height = abs(box[0][1] - box[2][1]) width = abs(box[0][0] - box[2][0]) if(height > width * 1.2): continue region.append(box) return regiondef detect(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) dilation = preprocess(gray) region = findTextRegion(dilation) for box in region: cv2.drawContours(img, [box], 0, (0, 255, 0), 2) cv2.namedWindow("img", cv2.WINDOW_NORMAL) cv2.imshow("img", img) cv2.imwrite("contours.png", img) cv2.waitKey(0) cv2.destroyAllWindows()if __name__ == '__main__': imagePath = sys.argv[1] img = cv2.imread(imagePath) detect(img)