OpenCV+CNN手写数字实时识别项目(五):手写数字识别终版


这个程序主要是解决了多个数字的问题,以及初版中因为数字离得太近导致识别效果不佳的问题

1. 导入库函数及设置相应的参数信息

这一部分没有什么变化。。

import cv2
import numpy as np
import tensorflow as tf

# #####设置参数#######################
widthImg = 640
heightImg = 480
kernal = np.ones((5, 5))
minArea = 800
# ##################################
cap = cv2.VideoCapture(2)
cap.set(3, widthImg)  # 设置参数,10为亮度
cap.set(4, heightImg)
cap.set(10, 150)

2. 图像处理函数

这里主要变化了getContours函数,由于有多个数字,因此将每一个数字的图像区域和其坐标组成一个元组,然后将几个元组放入一个列表。

def stackImages(scale, imgArray):
    rows = len(imgArray)
    cols = len(imgArray[0])
    rowsAvailable = isinstance(imgArray[0], list)
    width = imgArray[0][0].shape[1]
    height = imgArray[0][0].shape[0]
    if rowsAvailable:
        for x in range(0, rows):
            for y in range(0, cols):
                if imgArray[x][y].shape[:2] == imgArray[0][0].shape[:2]:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
                else:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]),
                                                None, scale, scale)
                if len(imgArray[x][y].shape) == 2: imgArray[x][y] = cv2.cvtColor(imgArray[x][y], cv2.COLOR_GRAY2BGR)
        imageBlank = np.zeros((height, width, 3), np.uint8)
        hor = [imageBlank] * rows
        hor_con = [imageBlank] * rows
        for x in range(0, rows):
            hor[x] = np.hstack(imgArray[x])
        ver = np.vstack(hor)
    else:
        for x in range(0, rows):
            if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
                imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
            else:
                imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None, scale, scale)
            if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
        hor = np.hstack(imgArray)
        ver = hor
    return ver


# 预处理函数
def preProccessing(img):
    imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1)
    imgCanny = cv2.Canny(imgBlur, 200, 200)
    imgDial = cv2.dilate(imgCanny, kernal, iterations=2)        # 膨胀操作
    imgThres = cv2.erode(imgDial, kernal, iterations=1)         # 腐蚀操作
    return imgThres


def getContours(img):
    x, y, w, h, xx, yy, ss = 0, 0, 10, 10, 20, 20, 10         # 因为图像大小不能为0
    imgGet = np.array([[], []])     # 不能为空
    contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)  # 检索外部轮廓
    for cnt in contours:  # 每一个轮廓线      hierarchy是层次关系,比如一个轮廓在另一个里面。。
        area = cv2.contourArea(cnt)
        # print(area)
        if area > minArea:  # 面积大于800像素为封闭图形
            cv2.drawContours(imgCopy, cnt, -1, (255, 0, 0), 3)  # 不要在原图上面画,-1是所有的轮廓
            peri = cv2.arcLength(cnt, True)  # 计算周长
            approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)  # 计算有多少个拐角
            x, y, w, h = cv2.boundingRect(approx)  # 得到外接矩形的大小

            a = (w+h)//2
            dd = abs((w-h)//2)      # 边框的差值

            # 这里直接取外接矩形的图像试试,就不要return坐标了
            imgGet = imgProcess[y:y+h, x:x+w]

            # cv2.rectangle(imgContour,(x, y),(x+w,y+h),(255,255,255),2)    # 白色
            if w <= h:  # 得到一个正方形框,边界往外扩充10像素,黑色边框
                imgGet = cv2.copyMakeBorder(imgGet, 10, 10, 10 + dd, 10 + dd, cv2.BORDER_CONSTANT,value=[0, 0, 0])
                xx = x-dd-10
                yy = y-10
                ss = h+20
                cv2.rectangle(imgCopy, (x-dd-10, y-10), (x+a+10, y+h+10), (0, 255, 0), 2)    # 看看框选的效果,在imgCopy中
                print(a+dd, h)
            else:               # 边界往外扩充10像素值
                imgGet = cv2.copyMakeBorder(imgGet, 10 + dd, 10 + dd, 10, 10, cv2.BORDER_CONSTANT, value=[0, 0, 0])
                xx = x-10
                yy = y-dd-10
                ss = w+20
                cv2.rectangle(imgCopy, (x-10, y-dd-10), (x+w+10, y+a+10), (0, 255, 0), 2)
                print(a+dd, w)

            Temptuple = (imgGet, xx, yy, ss)       # 将图像及其坐标放在一个元组里面,然后再放进一个列表里面就可以访问了
            Borderlist.append(Temptuple)

    return Borderlist

3. 实时检测(循环检测)

这里主要思想还是和初版一样,主要的不同在于是getContours返回的是一个列表,因此要先将数据取出来,再进行判断,识别的结果也在图像中显示出来。

# 重载模型
Saved_model = tf.keras.models.load_model('./my_models/MNIST_CNN_model.h5')
image_size = 28     # 图像尺寸28*28*1

while True:
    Borderlist = []         # 不同的轮廓图像及坐标
    Resultlist = []         # 识别结果
    i = 0
    success, img = cap.read()
    imgCopy = img.copy()
    imgCopy = imgCopy[100:400, 100:640]
    imgProcess = preProccessing(img)  # 可以很好地把图像取出来
    imgProcess = imgProcess[100:400, 100:640]  # 300*540 去掉水印
    Borderlist = getContours(imgProcess)

    if len(Borderlist) != 0:    # 不能为空
        for (imgRes, x, y, s) in Borderlist:
            if x >= 10 and y >= 10 and s > 15:
                # imgRes = imgProcess[y-5:y+s+10, x-5:x+s+10]     # 得到数字区域图片,注意先是y,再是x
                imgVert = np.expand_dims(imgRes, axis=2).repeat(1, axis=2)  # 前面得到的图片是二维的,这里给他加上第三维[28*28*1]
                # print(x,y,s)
                decode_img = tf.image.convert_image_dtype(imgVert, tf.float32)  # 转换为float32格式
                test_img = tf.image.resize(decode_img, [image_size, image_size])
                test_img = tf.reshape(test_img, [-1, image_size, image_size, 1])
                test_img = tf.keras.utils.normalize(test_img, axis=1)  # 归一化
                test_img = test_img.numpy()  # 需要转换为numpy类型

                predictions = Saved_model.predict(test_img)
                # print(predictions)
                result = np.argmax(predictions[0])
                Resultlist.append(result)
                # print("预测结果为:{}".format(result))
                result = str(result)
                cv2.putText(imgCopy,result,(x+s//2-5,y+s//2-5),cv2.FONT_HERSHEY_COMPLEX,1.5,(0,0,255),2)

                # 显示
                stackImg = stackImages(0.6, ([img, imgCopy], [imgProcess, imgRes]))
                cv2.imshow("Video", stackImg)
        for res in Resultlist:
            print("第{}个数字为: {}".format(i+1,res))
            i += 1
    else:
        print("区域内无数字")
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

4. 最终结果

实现了多个数字的识别。
在这里插入图片描述

  • 4
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值