博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于ML算法KNN与OpenCV的数独识别与自动填充___By 何子辰
阅读量:5098 次
发布时间:2019-06-13

本文共 14082 字,大约阅读时间需要 46 分钟。

最近在Github看到一个很有趣的案例,基于opencv与KNN的数独自动识别与填充算法,想自己写一下跑一遍,先上原地址:

【转】

上效果图:

Processing:

- 1:九宫格数字提取---Extract_nums.py
- **方法**: cv2.findContours(image, mode, method[, contours[, hierarchy[, offset] ] ]) → contours, hierarchy
~ Python中, findContours()接收如下参数并返回contours和hierarchy(等级)
~~1.image 源图像,一般为8为单通道图像,更具体来说,二值图像。其他情况暂且不论。
~~2.mode 轮廓检索模式,简要介绍几种:
(1)cv2.RETR_EXTERNAL 只检测外轮廓。对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1
(2)cv2.RETR_LIST 提取所有轮廓,并放置在list中,检测到的轮廓不建立等级关系。
(3)cv2.RETR_TREE 提取所有轮廓,建立网状的轮廓结构。
~~3.method 轮廓的近似办法,是提取轮廓上所有像素点,还是只提取关键的一些点。比如一条线段是提取所有点还是只提取两个端点。
~~4.contours 检测到的轮廓,为组成轮廓的点集。
~~5.hierarchy
(1)meanings: 什么是层级结构? 检测轮廓时,一个轮廓包含了另一个轮廓(同心圆)>>>外圆:父轮廓;内圆:子轮廓。
同一个级别又分为前一个轮廓,后一个轮廓。综上所述,hierachy表达不同轮廓之间的关系。
(2)对每一个轮廓有:[Next,Previous,First_Child,Parent])
(3)cv2.RETR_EXTERNAL 只检测外轮廓。对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1,即(2)中First_Child和Parent
都是-1

1  # -*- coding: UTF-8 -*- 2 import cv2 3 import glob as gb 4 # 九宫格,最外面包围所有的是0号轮廓,里面81个小方格就是0号轮廓的自轮廓, 5 # 而每一个已知数字的轮廓都是对应方格的子轮廓。 6  7 # 对每一个轮廓有:[Next,Previous,First_Child,Parent]) 8  9 #read the pic10 img = cv2.imread('images/001.jpg')11 #transfer the BGR to GRAY12 # gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)13 gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)14 15 #阈值分割16 ret, thresh = cv2.threshold(gray,200,255,1)17 18 #对二值图像进行膨胀操作???19 kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))20 dilated = cv2.dilate(thresh,kernel)21 22 #轮廓提取,cv2.RETR_TREE表示建立层级结构23 image, contours, hierarchy = cv2.findContours(dilated,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)24 print(hierarchy.shape)25 # cv2.namedWindow("img", cv2.WINDOW_NORMAL); 26 # cv2.imshow("img", image)27 # cv2.waitKey(0) 28 # 提取小方格,父轮廓都为0号29 boxes = []30 for i in range(len(hierarchy[0])):31     if hierarchy[0][i][3] == 0:32         boxes.append(hierarchy[0][i])33 34 # #提取数字35 number_boxes = []36 for j in range(len(boxes)):37     if boxes[j][2] != -1:38         #number_boxes.append(boxes[j])39         x,y,w,h = cv2.boundingRect(contours[boxes[j][2]])40         number_boxes.append([x,y,w,h])41         img = cv2.rectangle(img,(x-1,y-1),(x+w+1,y+h+1),(0,0,255),2)42         43 cv2.namedWindow("img", cv2.WINDOW_NORMAL); 44 cv2.imshow("img", img)45 cv2.waitKey(0)

- 2:数字识别

- a.数据收集和处理---build_datasets.py
- b.kNN数字识别---knn_recogntion.py
-(1) 这里用opencv自带的knn算法实现。我同时尝试了opencv自带的神经网络和SVM,发现还是kNN的效果最好。有兴趣的可以自己去尝试一下。也可能是我参数没调好。
-(2) -1.加载上面保存的样本和标签数据;
-2.分别用80个作为训练数据,20个作为测试数据;
-3.用opencv自带的knn训练模型;
-4.用训练好的模型识别测试数据中的数字;
-5.输出预测值和实际标签值。

1  # -*- coding: UTF-8 -*-  2 import numpy as np   3 import cv2   4 import glob as gb  5   6 #获取文件夹下原始数字图片  7 img_path = gb.glob("numbers\\*")  8 # img_path = cv2.imread()  9  10  11 k = 0 12 labels = [] 13 samples = [] 14  15 #1.遍历文件夹下原始数字图片 16 #2.对每张图片进行轮廓提取操作,只提取外围轮廓 17 #3.求轮廓外包矩形,并根据矩形大小信息筛选出所有的数字轮廓 18 #4.然后根据位置信息对数字框排序,显然第一排依次是12345,第二排依次是67890; 19 #5.提取每一个数字所在的矩形框,作为ROI取出 20 # 对每一个轮廓等级有:[Next,Previous,First_Child,Parent]) 21  22 # for path in range(10): 23 for path in img_path: 24     # print(path) 25     # img = cv2.imread('numbers/'+str(path+1)+'.jpg') 26     img = cv2.imread(path) 27     #img's shape: (pixel,pixel, 3 channels--> B,G,R) 28     # print("image's shape:{}"+str(img.shape)) 29     #转化为灰度图像 30     gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 31      32     # #高斯滤波 33     blur = cv2.GaussianBlur(gray,(5,5),0) 34  35     #adaptive Threshold自适应阈值 36     #将灰度图像转换为二值图像 37     thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2) 38  39     #提取轮廓 40     image,contours,hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 41     print("hierarchy's shape:{}".format(hierarchy.shape)) 42     # print("contours's shape:{}".format(contours.shape)) 43     # print(, sep, end, file, flush) 44  45     #求轮廓外包矩形,并根据矩形大小信息筛选出所有的数字轮廓 46     height,width = img.shape[:2] 47     w = width/5 48     recl_list = [] 49     list1 = [] 50     list2 = [] 51     # j = 0 52     # labels =[] 53     for cnt in contours: 54         #计算并返回指定点集的最小边界矩形 55         [x,y,w,h] = cv2.boundingRect(cnt) 56  57         if w>30 and h>(height/4): 58             if y<(height/2): 59                 list1.append([x,y,w,h]) 60             else: 61                 list2.append([x,y,w,h]) 62  63     # key = lambda 元素: 元素[字段索引] x:x[]可以随意修改,排序方式按照括号[]里面的维度进行 64     # [0] 第一维排序、 [1]第一维排序...以此类推             65     list1_sorted = sorted(list1, key=lambda t:t[0]) 66     list2_sorted = sorted(list2, key=lambda t:t[0]) 67  68     #提取出每一个数字所在的矩形框,作为ROI取出 69     for i in range(5): 70         [x1,y1,w1,h1] = list1_sorted[i] 71         [x2,y2,w2,h2] = list2_sorted[i] 72         number_roi1 = gray[y1:y1+h1, x1:x1+w1] #Cut the frame to size 73         number_roi2 = gray[y2:y2+h2, x2:x2+w2] #... 74  75         #数据预处理 76         #1. 把每一张ROI转换为40x20 ##########注意Opencv 里是宽x长 77         resized_roi1 = cv2.resize(number_roi1,(20,40)) 78         resized_roi2 = cv2.resize(number_roi2,(20,40)) 79  80         #2.阈值分割 灰度图-> 0-1二值图 81         thresh1 = cv2.adaptiveThreshold(resized_roi1,255,1,1,11,2) 82         thresh2 = cv2.adaptiveThreshold(resized_roi2,255,1,1,11,2) 83  84         #3.创建文件夹 85         # path_1 = "F:\\opencv_learning\\opencv_knn_soduko\\New_datasets\\"+str(i+1)+"\\"+str(k)+".jpg" 86         # j = 0 87         number_path1 = "datasets_hzc\\%s\\%d" % (str(i+1),k) + '.jpg' 88  89         j = i+6 90         if j == 10: 91             j = 0 92         # path_2 = "F:\\opencv_learning\\opencv_knn_soduko\\New_datasets\\"+str(j)+"\\"+str(k)+".jpg" 93         number_path2 = "datasets_hzc\\%s\\%d" % (str(j),k) + '.jpg' 94         k+=1 95  96         #4.Nomalized  97         # normalized_roi1 98         normalized_roil = thresh1/255 99         normalized_roi2 = thresh2/255100         # 5.write into the files101         # P.S Don't forget to annotate them, because u dont have to operate it twice102         # cv2.imwrite(number_path1,thresh1)103         # cv2.imwrite(number_path2,thresh2)104         #把处理完的二值图像展开成一行,以便knn处理105         sample_1 = normalized_roil.reshape((1,800))106         samples.append(sample_1[0])107         labels.append(float(i+1))108 109         #保存sample供训练用110         sample_2 = normalized_roi2.reshape((1,800))111         samples.append(sample_2[0])112         #把数字标签按照数字的保存顺序对应保存成训练用的数据113         labels.append(float(j))114 115         cv2.imwrite(number_path1,thresh1)116         cv2.imwrite(number_path2,thresh2)117         cv2.imshow("number",normalized_roil)118         cv2.waitKey(5)119         # cv2.imshow("1", thresh1)120         # cv2.imshow("2", thresh2)121         # cv2.waitKey(300)122     # print(list1_sorted)123     # print(list2_sorted)124     # cv2.imshow("train_pic", image)125     # cv2.waitKey(300)126 127 #保运数据供KNN用 k邻近算法128 print(np.array(labels).shape)129 130 print("\n"*3)131 samples = np.array(samples,np.float32)132 #(100,800) 1x100, 800133 print("train_data's dimension:{}".format(samples.shape))134 labels = np.array(labels,np.float32)135 labels = labels.reshape((labels.size,1))136 print("labels's dimension:{}".format(labels.shape))137 np.save('samples,npy',samples)138 np.save('label.npy',labels)

- 3.数独生成和求解

1 # -*- coding: UTF-8 -*-  2 import numpy as np  3 import cv2  4   5 # 利用K邻近算法来进行数字识别  6 # 1.加载上面保存的样本和标签数据;  7 # 2.分别用80个作为训练数据,20个作为测试数据;  8 # 3.用opencv自带的knn训练模型;  9 # 4.用训练好的模型识别测试数据中的数字; 10 # 5.输出预测值和实际标签值。 11  12 # print(labels.T) 13 # print(np.sort(labels, axis=0, kind='quicksort') 14 # print("samples's shape:{}".format(samples.shape)) 15  16 ## 数独求解算法,回溯法。来源见下面链接,有细微改动。 17 ## http://stackoverflow.com/questions/1697334/algorithm-for-solving-sudoku 18 def findNextCellToFill(grid, i, j): 19     for x in range(i,9): 20         for y in range(j,9): 21             if grid[x][y] == 0: 22                 return x,y 23     for x in range(0,9): 24         for y in range(0,9): 25             if grid[x][y] == 0: 26                 return x,y 27     return -1,-1 28  29 def isValid(grid, i, j, e): 30     rowOk = all([e != grid[i][x] for x in range(9)]) 31     if rowOk: 32         columnOk = all([e != grid[x][j] for x in range(9)]) 33         if columnOk: 34             # finding the top left x,y co-ordinates of the section containing the i,j cell 35             secTopX, secTopY = 3 *int(i/3), 3 *int(j/3) 36             for x in range(secTopX, secTopX+3): 37                 for y in range(secTopY, secTopY+3): 38                     if grid[x][y] == e: 39                         return False 40                 return True 41     return False 42  43 def solveSudoku(grid, i=0, j=0): 44     i,j = findNextCellToFill(grid, i, j) 45     if i == -1: 46         return True 47     for e in range(1,10): 48         if isValid(grid,i,j,e): 49             grid[i][j] = e 50             if solveSudoku(grid, i, j): 51                 return True 52             # Undo the current cell for backtracking 53             grid[i][j] = 0 54     return False 55  56 samples = np.load('F:/opencv_learning/opencv-soduko-master/samples.npy') 57 labels = np.load('F:/opencv_learning/opencv-soduko-master/label.npy') 58  59 k=80 60 train_label = labels[:k] 61 test_label = labels[k:] 62 train_input = samples[:k] 63 test_input = samples[k:] 64 print("train_label's shape:{}".format(train_label.shape)) 65 print("train_label's shape:{}".format(train_input.shape)) 66 print("test_label's shape:{}".format(test_label.shape)) 67 print("test_input's shape:{}".format(test_input.shape)) 68  69 #create the model 70 model = cv2.ml.KNearest_create() 71 model.train(train_input,cv2.ml.ROW_SAMPLE,train_label) 72  73 #retval:返回值类型说明 74 # retval,results,neigh_resp,dists = model.findNearest(test_input,1) 75 # string = results.ravel() 76 # print(string) 77 # print(test_label.reshape(1,len(test_label))[0]) 78 # print(string.shape) 79 # print("The original label:{}".format(test_label.reshape(1,len(test_label))[0])) 80 # print("The prediction:{}".format(str(string))) 81  82 # # print(test_label.T) 83 # count = 0 84 # string = string.reshape(1,len(string)) 85 # print(string.shape) 86 # string = np.array(string).T 87  88 # for index,value in enumerate(test_label): 89 #     if value != string[index]: 90 #         count+=1 91 # print(count) 92  93 # accuracy = 1 - (count / len(string)) 94 # print("The test accuracy:{}".format(accuracy)) 95  96 img = cv2.imread('./images/001.jpg') 97 gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 98  99 ##阈值分割100 ret,thresh = cv2.threshold(gray,200,255,1)101 102 # in ord to do morphological operation: returns a structuring element103 kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))104 #Brief dilated an image by a specific structuring element105 #膨胀106 dilated = cv2.dilate(thresh,kernel)107 108 #提取轮廓109 image, contours, hierarchy = cv2.findContours(dilated,cv2.RETR_TREE,\110             cv2.CHAIN_APPROX_SIMPLE)111 112 #提取81个小方格113 #hierarchy[Next,Previous,First_Child,Parent])114 boxes = []115 for i in range(len(hierarchy[0])):116     if hierarchy[0][i][3] == 0:117         boxes.append(hierarchy[0][i])118 119 print("81个小方格: dimension:{}".format(np.array(boxes).shape))120 # print(boxes)121 122 height,width = img.shape[:2]123 124 #"/9" 9行9列125 box_h = height/9126 box_w = height/9127 number_boxes = []128 129 #数独转化为零阵130 soduko = np.zeros((9,9),np.int32)131 132 for j in range(len(boxes)):133     if boxes[j][2] != -1:134         #Calculates and returns the minimal up-right bounding 135         #rectangle for the specified point set136         x,y,w,h = cv2.boundingRect(contours[boxes[j][2]])137         number_boxes.append([x,y,w,h])138         #process the data that was extracted139         number_roi = gray[y:y+h,x:x+w]140         #unit the size 141         resized_roi = cv2.resize(number_roi,(20,40))142         thresh1 = cv2.adaptiveThreshold(resized_roi,255,1,1,11,2)143         #归一化像素值144         normalized_roi = thresh1/255145 146         #展开一行让knn识别147         sample1 = normalized_roi.reshape((1,800))148         sample1 = np.array(sample1,np.float32)149 150         retval,result,neigh_resp,dists = model.findNearest(sample1,1)151         number = int(result.ravel()[0])152         # print(results.ravel())153 154         #识别结果155         cv2.putText(img,str(number),(x+w+1,y+h-20), 3, 2., (255, 0, 0), 2, cv2.LINE_AA)156         157         #矩阵中位置158         soduko[int(y/box_h)][int(x/box_w)] = number159 160         cv2.namedWindow("img", cv2.WINDOW_NORMAL)161         cv2.imshow("img", img)162         # cv2.imshow("normalized_roi",normalized_roi)163         cv2.waitKey(120)164 165 print("\n生成的数独\n")166 print(soduko)167 print("\n求解后的数独\n")168 169 #数独求解170 solveSudoku(soduko)171 print(soduko)172 173 print("\n验算:求每行每列的和\n")174 #map求和 map根据指定的函数对指定序列做映射175 #map(function, iterable)176 # row_sum = map(sum,soduko)177 # col_sum = map(sum,zip(*soduko))178 print(soduko.shape)179 # np.sum(0>>row, 1/-1>>column)180 row_sum = np.sum(soduko,axis = 0)181 col_sum = np.sum(soduko,axis = -1,keepdims=1)182 print(row_sum)183 print(col_sum.T)184 185 #把对应结果按照位置填充图片中186 for i in range(9):187     for j in range(9):188         x = int((i+0.25)*box_w) 189         y = int((j+0.5)*box_h)190         #brief Draws a text string.191         cv2.putText(img,str(soduko[j][i]),(x,y), 3, 2.5, (0, 0, 255), 2, cv2.LINE_AA)192 193 cv2.namedWindow("img",cv2.WINDOW_NORMAL)194 cv2.imshow("img", img)195 cv2.waitKey(10000)196         197 # np.sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, initial=np._NoValue)198 # print(list(row_sum))199 # print(list(col_sum))200 201 # cv2.imshow("origin",img)202 # cv2.waitKey(300)203 # cv2.imshow("gray",gray)204 # cv2.waitKey(300)205 # cv2.imshow("thresh",thresh)206 # cv2.imshow("dilated",dilated)207 # cv2.waitKey(0)208 # cv2.209 #SVM210 # C = 5211 # gamma = 0.5212 # model_svm = cv2.ml.SVM_create()213 # model_svm.setGamma(gamma)214 # model_svm.setC(C)215 216 # #LINEAR MODEL217 # model_svm.setKernel(cv2.ml.SVM_LINEAR)218 # model_svm.setType(cv2.ml.SVM_C_SVC)219 # model_svm.train(train_input, cv2.ml.ROW_SAMPLE, train_label)220 # predict_label = model_svm.predict(test_input)[1].ravel()221 # print(predict_label)222 # print(test_label.reshape(1,len(test_label))[0])

 

转载于:https://www.cnblogs.com/IrivingLoveLuna/p/10135706.html

你可能感兴趣的文章
招聘工作告一段落
查看>>
druid数据源的加密解密工具
查看>>
swfupload详解
查看>>
python-微博模拟登陆
查看>>
Python【11】【前端编程】- HTML基础
查看>>
nump库的简单函数介绍
查看>>
好程序员大数据点睛:Hadoop基础篇
查看>>
JVM内存模型和GC机制
查看>>
201571030323/201571030334《小学生四则运算练习软件需求说明》结对项目报告
查看>>
SequenceFile介绍
查看>>
安卓 代码混淆与打包
查看>>
AT&T汇编语言及其寻址方式
查看>>
ubuntu下 java、mysql、tomcat(ssl认证) 配置
查看>>
linux命名详解及其软件安装实例
查看>>
查看iOS沙盒(SanBox)文件
查看>>
数据结构与算法
查看>>
顺时针打印矩阵
查看>>
[转载]Chrome 与 Chrome OS 各版本下载集合
查看>>
面试微软前必须要读的十本书
查看>>
hadoop的安装和配置
查看>>