传统文本检测
当前应用中面对文本检测会遇到很多难点:
文本图像的背景多样化,很多背景可能像素情况与文本结构相似
文本的形状和方向多样化,可能图像中文本的摆放方向是倾斜的,横向的
文本的颜色,字体多样化
图像中受光照等环境因素影响
由于这些检测上的难点,传统基于opencv通过形态学、MSER+NMS的方法无法实现实际场景的文本检测。因此对于复杂场景下文本检测大多基于深度学习算法实现。
形态学:
import cv2 import numpy as np # 读取图片 imagePath = 'data.jpg' img = cv2.imread(imagePath) # 转化成灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 利用Sobel边缘检测生成二值图 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=2) # 查找轮廓和筛选文字区域 region = [] contours, hierarchy = cv2.findContours(dilation2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) for i in range(len(contours)): cnt = contours[i] # 计算轮廓面积,并筛选掉面积小的 area = cv2.contourArea(cnt) if (area < 1000): continue # 找到最小的矩形 rect = cv2.minAreaRect(cnt) print ("rect is: ") print (rect) # box是四个点的坐标 box = cv2.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.3): continue region.append(box) # 绘制轮廓 for box in region: cv2.drawContours(img, [box], 0, (0, 255, 0), 2) cv2.imshow('img', img) cv2.waitKey(0) cv2.destroyAllWindows()
MSER+NMS
MSER全称叫做最大稳定极值区域(MSER-Maximally Stable Extremal Regions),是一种检测图像中文本区域的传统图像算法,主要是基于分水岭的思想来对图像进行斑点(blob)区域检测。
MSER对灰度图像取阈值进行二值化处理,阈值从0到255依次进行递增,阈值的递增类似于分水岭算法中的水平面的上升,随着水平面的上升,有一些山谷和较矮的丘陵会被淹没,如果从天空往下看,则整个区域被分为陆地和水域两个部分,这类似于二值图像。图像中灰度值的不同就对应地势高低的不同,每个阈值都都会生成一个二值图。
NMS的基本思想是将所有框按得分进行排序,然后无条件保留其中得分最高的框,然后遍历其余框找到和当前最高分的框的重叠面积(IOU)大于一定阈值的框,并删除。然后继续这个过程,找另一个得分高的框,再删除和其IOU大于阈值的框,一直循环直到所有的框都被处理。
NMS流程:
根据候选框的类别分类概率(得分),按最高到最低将BBox排序,例如:A>B>C>D>E>F
先标记最大概率矩形框A是要保留下来的,即A的分数最高,则无条件保留
将B~E分别与A求重叠率IoU(两框的交并比),假设B、D与A的IoU大于设定的阈值,那么B和D可以认为是重复标记被剔除
继续从剩下的矩形框C、E、F中选择概率最大的C,标记为要无条件保留下来的框,然后分别计算C与E、F的重叠度,扔掉重叠度超过设定阈值的矩形框
就这样一直重复下去,直到剩下的矩形框没有了,得到所有要保留下来的矩形框
MSER实现:
# 读取图片 imagePath = 'data.jpg' img = cv2.imread(imagePath) # 灰度化 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) vis = img.copy() orig = img.copy() # 调用 MSER 算法 mser = cv2.MSER_create() regions, _ = mser.detectRegions(gray) # 获取文本区域 hulls = [cv2.convexHull(p.reshape(-1, 1, 2)) for p in regions] # 绘制文本区域 cv2.polylines(img, hulls, 1, (0, 255, 0)) cv2.imshow('img', img) # 将不规则检测框处理成矩形框 keep = [] for c in hulls: x, y, w, h = cv2.boundingRect(c) keep.append([x, y, x + w, y + h]) cv2.rectangle(vis, (x, y), (x + w, y + h), (255, 255, 0), 1) cv2.imshow("hulls", vis)
NMS实现:
# NMS 方法(Non Maximum Suppression,非极大值抑制) def nms(boxes, overlapThresh): if len(boxes) == 0: return [] if boxes.dtype.kind == "i": boxes = boxes.astype("float") pick = [] # 取四个坐标数组 x1 = boxes[:, 0] y1 = boxes[:, 1] x2 = boxes[:, 2] y2 = boxes[:, 3] # 计算面积数组 area = (x2 - x1 + 1) * (y2 - y1 + 1) # 按得分排序(如没有置信度得分,可按坐标从小到大排序,如右下角坐标) idxs = np.argsort(y2) # 开始遍历,并删除重复的框 while len(idxs) > 0: # 将最右下方的框放入pick数组 last = len(idxs) - 1 i = idxs[last] pick.append(i) # 找剩下的其余框中最大坐标和最小坐标 xx1 = np.maximum(x1[i], x1[idxs[:last]]) yy1 = np.maximum(y1[i], y1[idxs[:last]]) xx2 = np.minimum(x2[i], x2[idxs[:last]]) yy2 = np.minimum(y2[i], y2[idxs[:last]]) # 计算重叠面积占对应框的比例,即 IoU w = np.maximum(0, xx2 - xx1 + 1) h = np.maximum(0, yy2 - yy1 + 1) overlap = (w * h) / area[idxs[:last]] # 如果 IoU 大于指定阈值,则删除 idxs = np.delete(idxs, np.concatenate(([last], np.where(overlap > overlapThresh)[0]))) return boxes[pick].astype("int")
深度学习文本检测分类
目前基于在深度学习的文本检测方法主要包括如下:
- 基于候选框的文本检测
- 基于分割的文本检测
- 基于两者方法混合文本检测
基于候选框的文本检测
思路:基于候选框文本检测,是利用像素特征提取,用若干个先验框产生一些对应的候选文本框。再经过非最大抑制(NMS)得到最终的预测结果
基于分割的文本检测
基于分割的文本检测,其基本思路是通过分割网络结构进行像素级别的语义分割,再基于分割的结果构建文本行
基于混合的文本检测