利用opencv实现对图片上的银行卡上的卡号识别,并把识别出的数字标注在图片上。解决思路:
因为轮廓提取函数只接受二值图像,所以先灰度化,二值化:
ref = cv2.cvtCOLOR(img,cv2.COLOR_BGR2GRAY)
#二值化图像(将超过10的像素值取255,低于10的取0)
ref= cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
src,dst = cv2.threshold(img,thresh,maxVal,type)
见:图像阈值计算
提取并绘制轮廓,:
# findContoursh只接受二值图像,黑白图像,不是灰度图
# cv2.RETR_EXTERNAL只检测外轮廓,
# cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
#返回的list中每个元素都是图像中的一个轮廓
ref_,refCnts,hierarchy = cv2.findContours(ref.copy(),
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,refCnts,-1,(0,0,255),3)
for (i, c) in enumerate(refCnts):
(x,y,w,h) = cv2.boundingRect(c)
roi = ref[y:y+h,x:x+w]
roi = cv2.resize(roi,(57,88))
digits[i] = roi
读取后,灰度化。
礼帽操作,突出明亮区域
tophat = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
morphologyEx形态学操作,见:图像形态学操作
用sobel因子计算梯度
gradX = cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=1,
dy=0,ksize=-1)
#梯度有负有在正,求绝对值
gradX = np.absolute(gradX)
#将值标准化
(minVal,maxVal) = (np.min(gradX),np.max(gradX))
gradX = (255*((gradX-minVal)/(maxVal-minVal)))
#去除噪音
gradX = gradX.astype("uint8")
图像梯度见: 图像梯度
闭操作连接数字的轮廓
#通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradX = cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel)
#thresh_otsu会自动寻找合适的阈值,适合双峰,需把阈值参数设为0
thresh = cv2.threshold(gradX,0,255,
cv2.THRESH_BINARY|cv2.THRESH)[1]
#计算轮廓,只检测外轮廓,只保留终点坐标
thresh_, threshCnts, hierarchy = cv2.findContours(thresh.copy(),
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
for (i,c) in enumerate(cnts):
#计算矩形
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
#筛选属于数值的矩形,四个数字为一个轮廓
if ar >2.5 and ar < 4.0:
if(w >40 and w <55) and (h > 10 and h<20):
locs.append((x,y,w,h))
# 将符合的轮廓从左到右排序
locs = sorted(locs,key=lambda x:x[0])
output = []
#最大循环,所有数字组
for (i,(gX,gY,gW,gH)) in enumerate(locs):
gropOutPut = []
# 根据坐标提取每组
print(gX,gY,gW,gH)
#128*800(w*h)图片在矩阵存储是800*128(h,w)
#此处-5和+5都是位移,保证了一组数据不会被重复选取
group = gray[gY-5:gY+gH+5,gX-5:gX+gW+5]
cv_show('group',group)
#提取每组的轮廓,并对其进行排序
group = cv2.threshold(group,0,255,cv2.THRESH_BINARY
|cv2.THRESH_OTSU)[1]
group_,digitCnts,hierarchy = cv2.findContours(group,cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
digitCnts = myutils.sort_contours(digitCnts,"left-to-right")[0]
#每个数字组,4个数字
for c in digitCnts:
#找到当前数值的轮廓,resize,和模板一样
(x,y,w,h) = cv2.boundingRect(c)
roi = group[y:y+h,x:x+w]
roi = cv2.resize(roi,(57,88))
cv_show('roi',roi)
# 计算匹配得分
scores =[]
# digits是每个数字的模板,
# 在所有的数字模板中进行匹配 TM_CCORR:计算相关性,计算出来的值越大,越相关
for(digit,digitROI) in digits.items():
result = cv2.matchTemplate(roi,digitROI,
cv2.TM_CCOEFF)
# min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
(_,score,_,_) = cv2.minMaxLoc(result)
scores.append(score)
gropOutPut.append(str(np.argmax(scores)))
print(gropOutPut)
#确实是靠 确定对角线 来画矩形的。
# cv2.rectangle(img, (bbox.left, bbox.top), (bbox.right, bbox.bottom), (0,0,255), 2)
cv2.rectangle(image,(gX-5,gY-5),(gX+gW+5,gY+gH+5),(0,0,255),1)
cv2.putText(image,"".join(gropOutPut),(gX,gY-15),
cv2.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)
output.extend(gropOutPut)