├── rowChars └── mkdir.bat ├── label.npy ├── readme.txt ├── samples.npy ├── font ├── Thumbs.db ├── cuqian.TTF └── kaijian.ttf ├── samples ├── 1.jpg ├── 10.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg ├── 7.jpg ├── 8.jpg └── 9.jpg ├── phrases ├── phrase.jpg └── phrase1.jpg ├── chars └── mkdir.bat ├── README.md ├── sampleGenerator.py └── riddle.py /rowChars/mkdir.bat: -------------------------------------------------------------------------------- 1 | mkdir one two three 2 | -------------------------------------------------------------------------------- /label.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/label.npy -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/readme.txt -------------------------------------------------------------------------------- /samples.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples.npy -------------------------------------------------------------------------------- /font/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/font/Thumbs.db -------------------------------------------------------------------------------- /font/cuqian.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/font/cuqian.TTF -------------------------------------------------------------------------------- /samples/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples/1.jpg -------------------------------------------------------------------------------- /samples/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples/10.jpg -------------------------------------------------------------------------------- /samples/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples/2.jpg -------------------------------------------------------------------------------- /samples/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples/3.jpg -------------------------------------------------------------------------------- /samples/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples/4.jpg -------------------------------------------------------------------------------- /samples/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples/5.jpg -------------------------------------------------------------------------------- /samples/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples/6.jpg -------------------------------------------------------------------------------- /samples/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples/7.jpg -------------------------------------------------------------------------------- /samples/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples/8.jpg -------------------------------------------------------------------------------- /samples/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/samples/9.jpg -------------------------------------------------------------------------------- /font/kaijian.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/font/kaijian.ttf -------------------------------------------------------------------------------- /phrases/phrase.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/phrases/phrase.jpg -------------------------------------------------------------------------------- /phrases/phrase1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayue801/crossword-puzzle--idiom/HEAD/phrases/phrase1.jpg -------------------------------------------------------------------------------- /chars/mkdir.bat: -------------------------------------------------------------------------------- 1 | mkdir qing feng ming yue lao ku gong gao bian shi fei qin gu shan liu shui xin 2 | mkdir ru zhi bu zi feng1 bi wu ke yi shuang fei1 lian hua nu fang hu gui 3 | mkdir hou e qi pu zhi huo xiu -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # crossword-puzzle--idiom 2 | 中文成语填字游戏,使用opencv 3 | 本中文填字游戏只适合于成语,并且图片,算法什么的也只适合于本项目中所提供的。 4 | ### 博客介绍: 5 | * [opencv3+python3.5成语填字游戏(一)印刷体汉字的分割](http://blog.csdn.net/weixin_41553161/article/details/78974832) 6 | * [opencv3+python3.5成语填字游戏(二)填字图片汉字提取和识别](http://blog.csdn.net/weixin_41553161/article/details/78984489) 7 | * [opencv3+python3.5成语填字游戏(三)成语填字游戏解密算法](http://blog.csdn.net/weixin_41553161/article/details/79166295) 8 | -------------------------------------------------------------------------------- /sampleGenerator.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import glob as gb 3 | import cv2 4 | import numpy as np 5 | 6 | #针对的是印刷版的汉字,所以采用了投影法分割 7 | #此函数是行分割,结果是一行文字 8 | def YShadow(path): 9 | img = cv2.imread(path) 10 | gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 11 | height,width = img.shape[:2] 12 | 13 | #blur = cv2.GaussianBlur(gray,(5,5),0) 14 | 15 | blur = cv2.blur(gray,(8,8)) 16 | thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2) 17 | temp = thresh 18 | 19 | if(width > 500 and height > 400): 20 | kernel = np.ones((5,5),np.uint8) #卷积核 21 | dilation = cv2.dilate(thresh,kernel,iterations = 1) #膨胀操作使得单个文字图像被黑像素填充 22 | temp = dilation 23 | 24 | ''' 25 | cv2.imshow('image',temp) 26 | cv2.waitKey(0) 27 | cv2.destroyAllWindows() 28 | ''' 29 | 30 | perPixelValue = 1 #每个像素的值 31 | projectValArry = np.zeros(width, np.int8) #创建一个用于储存每列黑色像素个数的数组 32 | 33 | 34 | for i in range(0,height): 35 | for j in range(0,width): 36 | perPixelValue = temp[i,j] 37 | if (perPixelValue == 255): #如果是黑字 38 | projectValArry[i] += 1 39 | # print(projectValArry[i]) 40 | 41 | canvas = np.zeros((height,width), dtype="uint8") 42 | 43 | for i in range(0,height): 44 | for j in range(0,width): 45 | perPixelValue = 255 #白色背景 46 | canvas[i, j] = perPixelValue 47 | 48 | 49 | for i in range(0,height): 50 | for j in range(0,projectValArry[i]): 51 | perPixelValue = 0 #黑色直方图投影 52 | canvas[i, width-j-1] = perPixelValue 53 | ''' 54 | cv2.imshow('canvas',canvas) 55 | cv2.waitKey(0) 56 | cv2.destroyAllWindows() 57 | ''' 58 | 59 | list = [] 60 | startIndex = 0 #记录进入字符区的索引 61 | endIndex = 0 #记录进入空白区域的索引 62 | inBlock = 0 #是否遍历到了字符区内 63 | 64 | 65 | for i in range(height): 66 | if (inBlock == 0 and projectValArry[i] != 0): 67 | inBlock = 1 68 | startIndex = i 69 | elif (inBlock == 1 and projectValArry[i] == 0): 70 | endIndex = i 71 | inBlock = 0 72 | subImg = gray[startIndex:endIndex+1,0:width] #endIndex+1 73 | #print(startIndex,endIndex+1) 74 | list.append(subImg) 75 | #print(len(list)) 76 | return list 77 | 78 | 79 | #对行字进行单个字的分割 80 | def XShadow(path): 81 | img = cv2.imread(path) 82 | gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 83 | height,width = img.shape[:2] 84 | # print(height,width) 85 | #blur = cv2.GaussianBlur(gray,(5,5),0) 86 | 87 | blur = cv2.blur(gray,(8,8)) 88 | thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2) 89 | 90 | if(width > 500): 91 | kernel = np.ones((4, 4),np.uint8) #卷积核 92 | else: 93 | kernel = np.ones((2, 2),np.uint8) #卷积核 94 | dilation = cv2.dilate(thresh,kernel,iterations = 1) #膨胀操作使得单个文字图像被黑像素填充 95 | 96 | ''' 97 | cv2.imshow('image',thresh) 98 | cv2.waitKey(0) 99 | cv2.destroyAllWindows() 100 | ''' 101 | 102 | perPixelValue = 1 #每个像素的值 103 | projectValArry = np.zeros(width, np.int8) #创建一个用于储存每列黑色像素个数的数组 104 | 105 | 106 | for i in range(0,width): 107 | for j in range(0,height): 108 | perPixelValue = dilation[j,i] 109 | if (perPixelValue == 255): #如果是黑字 110 | projectValArry[i] += 1 111 | # print(projectValArry[i]) 112 | 113 | canvas = np.zeros((height,width), dtype="uint8") 114 | 115 | for i in range(0,width): 116 | for j in range(0,height): 117 | perPixelValue = 255 #白色背景 118 | canvas[j, i] = perPixelValue 119 | 120 | 121 | for i in range(0,width): 122 | for j in range(0,projectValArry[i]): 123 | perPixelValue = 0 #黑色直方图投影 124 | canvas[height-j-1, i] = perPixelValue 125 | ''' 126 | cv2.imshow('canvas',canvas) 127 | cv2.waitKey(0) 128 | cv2.destroyAllWindows() 129 | ''' 130 | 131 | list = [] 132 | startIndex = 0 #记录进入字符区的索引 133 | endIndex = 0 #记录进入空白区域的索引 134 | inBlock = 0 #是否遍历到了字符区内 135 | 136 | 137 | for i in range(width): 138 | if (inBlock == 0 and projectValArry[i] != 0): 139 | inBlock = 1 140 | startIndex = i 141 | elif (inBlock == 1 and projectValArry[i] == 0): 142 | endIndex = i 143 | inBlock = 0 144 | #subImg = gray[0:height, startIndex:endIndex+1] #endIndex+1 145 | #print(startIndex,endIndex+1) 146 | list.append([startIndex, 0, endIndex-startIndex-1, height]) 147 | #print(len(list)) 148 | return list 149 | 150 | chars = ['qing', 'feng', 'ming', 'yue', 'lao', 'ku', 'gong', 'gao', 'bian', 'shi', 'fei', 'qin', 'gu', 'shan', 'liu', 'shui', 'xin', 151 | 'ru', 'zhi', 'bu', 'zi', 'feng1', 'bi', 'wu', 'ke', 'yi', 'shuang', 'fei1', 'lian', 'hua', 'nu', 'fang', 'hu', 'gui', 152 | 'hou', 'e','qi','pu', 'zhi', 'huo', 'xiu'] 153 | rowChars=['one','two','three'] 154 | labels=[] 155 | samples=[] 156 | #实际的分割函数,并生成行字与单个字的图片 157 | def createImgLabel(realpath, k): 158 | n = 0 159 | listY = YShadow(realpath) 160 | for i in range(len(listY)): 161 | path = 'rowChars\\'+ (rowChars[i]) + '\\'+ (str(k)) + '.jpg' 162 | #j += 1 163 | cv2.imwrite(path,listY[i]) 164 | listX = XShadow(path) 165 | list_sorted = sorted(listX,key = lambda t : t[0]) 166 | img = cv2.imread(path) 167 | #print(list_sorted) 168 | ''' 169 | cv2.imshow('canvas1',img) 170 | cv2.waitKey(0) 171 | cv2.destroyAllWindows() 172 | ''' 173 | gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 174 | print(len(listX)) 175 | for m in range(len(listX)): 176 | [x1,y1,w1,h1] = list_sorted[m] 177 | #print(x1,y1,w1,h1) 178 | ## 切割出每一个数字 179 | number_roi = gray[y1:y1+h1, x1:x1+w1] #Cut the frame to size 180 | ## 对图片进行大小统一和预处理 181 | #blur = cv2.GaussianBlur(number_roi,(5,5),0) 182 | # u = cv2.adaptiveThreshold(blur,255,1,1,11,2) 183 | 184 | 185 | resized_roi=cv2.resize(number_roi,(30,30)) 186 | thresh = cv2.adaptiveThreshold(resized_roi,255,1,1,11,2) 187 | #归一化处理 188 | normalized_roi = thresh/255 189 | ''' 190 | cv2.imshow('thresh',number_roi) 191 | cv2.waitKey(0) 192 | cv2.destroyAllWindows() 193 | ''' 194 | # print(n) 195 | 196 | sub_path = 'chars\\'+ (chars[n]) + '\\'+(str(k))+'.jpg' 197 | cv2.imwrite(sub_path,thresh) 198 | #print(len(normalized_roi)*len(normalized_roi[0])) 199 | ## 把图片展开成一行,然后保存到samples 200 | ## 保存一个图片信息,保存一个对应的标签 201 | sample1 = normalized_roi.reshape((1,900)) 202 | samples.append(sample1[0]) 203 | labels.append(float(n+1)) 204 | n += 1 205 | k += 1 206 | #print(n) 207 | ''' 208 | cv2.imshow('canvas1',gray) 209 | cv2.waitKey(0) 210 | cv2.destroyAllWindows() 211 | ''' 212 | 213 | 214 | 215 | 216 | ## 获取samples文件夹下所有文件路径 217 | img_path = gb.glob("samples\\*") 218 | 219 | ## 对每一张图片进行处理 220 | k = 0 221 | for path in img_path: 222 | print(path) 223 | createImgLabel(path, k) 224 | k += 41 225 | 226 | 227 | 228 | ## 这里还是把它们保存成了np.array... 229 | samples = np.array(samples,np.float32) 230 | labels = np.array(labels,np.float32) 231 | labels = labels.reshape((labels.size,1)) 232 | 233 | np.save('samples.npy',samples) 234 | np.save('label.npy',labels) 235 | ''' 236 | ## 保存完加载一下试试 237 | test = np.load('samples.npy') 238 | label = np.load('label.npy') 239 | print(test[0]) 240 | print(test[0].shape) 241 | print('label: ', label[0]) 242 | ''' 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /riddle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Dec 30 21:43:33 2017 4 | 5 | @author: Administrator 6 | """ 7 | import numpy as np 8 | import cv2 9 | from PIL import Image, ImageDraw, ImageFont 10 | 11 | chengyus=[['劳','苦','功','高'], 12 | ['非','亲','非','故'], 13 | ['山','清','水','秀'], 14 | ['明','辨','是','非'], 15 | ['清','风','明','月'], 16 | ['比','屋','可','封'], 17 | ['心','如','止','水'], 18 | ['高','山','流','水'], 19 | ['步','步','莲','花'], 20 | ['故','步','自','封'], 21 | ['后','起','之','秀'], 22 | ['比','翼','双','飞'], 23 | ['心','花','怒','放'], 24 | ['放','虎','归','山'], 25 | ['飞','蛾','扑','火']] 26 | #寻找横向的四字成语方格 27 | def findXFour(mi, i, j): 28 | if (j+1 == 10 or mi[i][j+1] == '0') and j-1>=0 and mi[i][j-1] != '0': 29 | return [[mi[i][j-3],mi[i][j-2],mi[i][j-1],mi[i][j]], 30 | [i,j-3,i,j-2,i,j-1,i,j]] 31 | elif (j+2 ==10 or (0 if j+2>10 else mi[i][j+2] == '0')) and j-1>=0 and mi[i][j-1] != '0': 32 | return [[mi[i][j-2],mi[i][j-1],mi[i][j],mi[i][j+1]], 33 | [i,j-2,i,j-1,i,j,i,j+1]] 34 | elif (j+3 ==10 or (0 if j+3>10 else mi[i][j+3] == '0')) and j-1>=0 and mi[i][j-1] != '0': 35 | return [[mi[i][j-1],mi[i][j],mi[i][j+1],mi[i][j+2]], 36 | [i,j-1,i,j,i,j+1,i,j+2]] 37 | elif (j+4 ==10 or (0 if j+4>10 else mi[i][j+4] == '0')) and (mi[i][j+1] != '0'): 38 | return [[mi[i][j],mi[i][j+1],mi[i][j+2],mi[i][j+3]], 39 | [i,j,i,j+1,i,j+2,i,j+3]] 40 | else: 41 | return [] 42 | #寻找纵向的四字成语方格 43 | def findYFour(mi, i, j): 44 | if (i+1==10 or mi[i+1][j] == '0') and i-1>=0 and mi[i-1][j] != '0': 45 | return [[mi[i-3][j],mi[i-2][j],mi[i-1][j],mi[i][j]], 46 | [i-3,j,i-2,j,i-1,j,i,j]] 47 | elif (i+2==10 or (0 if i+2>10 else mi[i+2][j] == '0')) and i-1>=0 and mi[i-1][j] != '0': 48 | return [[mi[i-2][j],mi[i-1][j],mi[i][j],mi[i+1][j]], 49 | [i-2,j,i-1,j,i,j,i+1,j]] 50 | elif (i+3==10 or (0 if i+3>10 else mi[i+3][j] == '0')) and i-1>=0 and mi[i-1][j] != '0': 51 | return [[mi[i-1][j],mi[i][j],mi[i+1][j],mi[i+2][j]], 52 | [i-1,j,i,j,i+1,j,i+2,j]] 53 | elif (i+4==10 or (0 if i+4>10 else mi[i+4][j] == '0')) and (mi[i+1][j] == '0'): 54 | return [[mi[i][j],mi[i+1][j],mi[i+2][j],mi[i+3][j]], 55 | [i,j,i+1,j,i+2,j,i+3,j]] 56 | else: 57 | return [] 58 | 59 | 60 | #改变对应方格的原生成成语填字矩阵 61 | def changeMi(res, micopy): 62 | counts= [] 63 | for j in range(len(chengyus)): 64 | count = 0 65 | if res[0][0] == chengyus[j][0]: 66 | count += 1 67 | if res[0][1] == chengyus[j][1]: 68 | count += 1 69 | if res[0][2] == chengyus[j][2]: 70 | count += 1 71 | if res[0][3] == chengyus[j][3]: 72 | count += 1 73 | counts.append(count) 74 | m = counts.index(max(counts)) 75 | #print(max(counts)) 76 | micopy[res[1][0]][res[1][1]] = chengyus[m][0] 77 | micopy[res[1][2]][res[1][3]] = chengyus[m][1] 78 | micopy[res[1][4]][res[1][5]] = chengyus[m][2] 79 | micopy[res[1][6]][res[1][7]] = chengyus[m][3] 80 | chengyus.remove([chengyus[m][0],chengyus[m][1],chengyus[m][2],chengyus[m][3]]) 81 | 82 | #判断res此四字成语是否在nodes里面存在 83 | def isExist(nodes,res): 84 | for i in range(len(nodes)): 85 | if(nodes[i][0][0] == res[0][0] and nodes[i][0][1] == res[0][1] 86 | and nodes[i][0][2] == res[0][2] and nodes[i][0][3] == res[0][3]): 87 | return 1 88 | return 0 89 | 90 | #实际的查找成语方格并修改原成语矩阵的函数 91 | def getResult(mi, i,j, nodes, micopy): 92 | temp = findXFour(mi, i, j) 93 | res = (len(temp) != 0 and temp or findYFour(mi, i, j)) 94 | 95 | if(len(res) > 0 and not isExist(nodes, res)): 96 | nodes.append(res) 97 | changeMi(res, micopy) 98 | 99 | import copy 100 | #总体的揭解谜函数 101 | def solve(mi): 102 | nodes = [] 103 | micopy = copy.deepcopy(mi) #对象拷贝,深拷贝 104 | for i in range(len(mi)): 105 | for j in range(len(mi[i])): 106 | if(mi[i][j] != '0'): 107 | getResult(mi, i,j, nodes, micopy) 108 | # print(nodes) 109 | return micopy 110 | 111 | 112 | ## 训练knn模型 113 | samples = np.load('samples.npy') 114 | labels = np.load('label.npy') 115 | 116 | 117 | #print(len(samples)) 118 | k = 1102 119 | train_label = labels[:k] 120 | train_input = samples[:k] 121 | test_input = samples[k:] 122 | test_label = labels[k:] 123 | 124 | 125 | ''' 126 | from sklearn.neighbors import KNeighborsClassifier 127 | # fit a k-nearest neighbor model to the data 128 | model = KNeighborsClassifier() 129 | 130 | from sklearn.svm import SVC 131 | # fit a SVM model to the data 132 | model = SVC() 133 | model.fit(samples, labels) 134 | ''' 135 | ''' 136 | # make predictions 137 | predicted = model.predict(test_input) 138 | print(predicted) 139 | print(test_label.reshape(1,len(test_label))[0]) 140 | ''' 141 | #创建knn对象并训练样本 142 | model = cv2.ml.KNearest_create() 143 | model.train(samples,cv2.ml.ROW_SAMPLE,labels) 144 | 145 | ''' 146 | retval, results, neigh_resp, dists = model.findNearest(test_input, 1) 147 | string = results.ravel() 148 | print(string) 149 | print(test_label.reshape(1,len(test_label))[0]) 150 | ''' 151 | #读入原图像 152 | img = cv2.imread('phrases\\phrase.jpg') 153 | #灰度图像生成 154 | gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 155 | ## 阈值分割 156 | ret,thresh = cv2.threshold(gray,200,255,1) 157 | ##膨胀处理 158 | kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(5, 5)) 159 | dilated = cv2.dilate(thresh,kernel) 160 | 161 | ## 轮廓提取 162 | image, contours, hierarchy = cv2.findContours(dilated,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) 163 | 164 | ''' 165 | cv2.imshow("img", dilated) 166 | cv2.waitKey(0) 167 | cv2.destroyAllWindows() 168 | ''' 169 | 170 | 171 | ## 提取100个小方格 172 | boxes = [] 173 | indexs = [] 174 | #print(len(hierarchy[0])) 175 | for i in range(len(hierarchy[0])): 176 | if hierarchy[0][i][3] == 0: 177 | boxes.append(hierarchy[0][i]) 178 | indexs.append(i) 179 | #print(boxes) 180 | #print(indexs) 181 | #获取原图像长宽 182 | height,width = img.shape[:2] 183 | box_h = height/10 184 | box_w = width/10 185 | number_boxes = [] 186 | numbers = [] 187 | 188 | miyu = [[0 for i in range(10)] for i in range(10)] 189 | ## 填字游戏初始化为零阵 190 | for i in range(10): 191 | for j in range(10): 192 | miyu[i][j] = "0" 193 | 194 | 195 | hanzis = ['清', '风', '明', '月', '劳', '苦', '功', '高', '辨', '是', '非', '亲', '故', '山', '流', '水', '心', 196 | '如', '止', '步', '自', '封', '比', '屋', '可', '翼', '双', '飞', '莲', '花', '怒', '放', '虎', '归', 197 | '后', '蛾','起','扑', '之', '火', '秀'] 198 | 199 | 200 | #print(boxes) 201 | #为了在图片上显示中文的一系列操作 202 | cv2_im = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # cv2和PIL中颜色的hex码的储存顺序不同 203 | pil_im = Image.fromarray(cv2_im) 204 | draw = ImageDraw.Draw(pil_im) # 括号中为需要打印的canvas,这里就是在图片上直接打印 205 | font = ImageFont.truetype("font\\kaijian.ttf", 25, encoding="utf-8") # 第一个参数为字体文件路径,第二个为字体大小 206 | n=0 207 | 208 | #提取方格中的汉字 209 | for j in range(len(boxes)): 210 | if boxes[j][2] == -1: 211 | x,y,w,h = cv2.boundingRect(contours[indexs[j]]) 212 | number_boxes.append([x,y,w,h]) 213 | #cv2.rectangle(img,(x-1,y-1),(x+w-10,y+h-10),(0,0,255),1) 214 | centerColor = img[round((2*y+h)/2),round((2*x+w)/2)] 215 | #print(centerColor) 216 | if(centerColor[0] > 200): #根据原图片当前位置的像素值区分出黄色格与白色格 217 | #print(y/box_h,round(y/box_h),x/box_w,round(x/box_w)) 218 | miyu[round(y/box_h)][round(x/box_w)] = "1" #白色空格填‘1’ 219 | elif boxes[j][2] != -1: 220 | # print("有%d"%(j)) 221 | x,y,w,h = cv2.boundingRect(contours[boxes[j][2]]) 222 | #print(x,y,w,h) 223 | # x,y,w,h = cv2.boundingRect(contours[boxes[j][2]]) 224 | #print(x,y,w,h) 225 | number_boxes.append([x,y,w,h]) 226 | #cv2.rectangle(img,(x-1,y-1),(x+w+1,y+h+1),(0,255,0),1) 227 | #img = cv2.drawContours(img, contours, boxes[j][2], (0,255,0), 1) 228 | ## 对提取的数字进行处理 229 | number_roi = gray[y:y+h, x:x+w] 230 | ## 统一大小 231 | resized_roi=cv2.resize(number_roi,(30,30)) 232 | thresh1 = cv2.adaptiveThreshold(resized_roi,255,1,1,11,2) 233 | ## 归一化像素值 234 | normalized_roi = thresh1/255. 235 | ''' 236 | cv2.imshow("thresh1", thresh1) 237 | cv2.waitKey(0) 238 | cv2.destroyAllWindows() 239 | ''' 240 | ## 展开成一行让knn识别 241 | sample1 = normalized_roi.reshape((1,len(normalized_roi)*len(normalized_roi[0]))) 242 | sample1 = np.array(sample1,np.float32) 243 | 244 | ## knn识别 245 | retval, results, neigh_resp, dists = model.findNearest(sample1, 1) 246 | number = int(results.ravel()[0]) 247 | #print(number) 248 | #numbers.append(number) 249 | 250 | ''' 251 | ### 252 | results = model.predict(sample1) 253 | number = int(results.ravel()[0]) 254 | ''' 255 | # 第一个参数为打印的坐标,第二个为打印的文本,第三个为字体颜色,第四个为字体 256 | draw.text((x+(w/2)+10,y-10), str(hanzis[number-1]), (0, 0, 255), font=font) 257 | 258 | ## 求在矩阵中的位置 259 | miyu[round(y/box_h)][round(x/box_w)] = str(hanzis[number-1]) 260 | 261 | cv2_text_im = cv2.cvtColor(np.array(pil_im), cv2.COLOR_BGR2RGB) 262 | cv2.namedWindow("img", cv2.WINDOW_NORMAL) 263 | cv2.imshow("img", cv2_text_im) 264 | cv2.waitKey(0) 265 | cv2.destroyAllWindows() 266 | 267 | 268 | 269 | print("\n生成的填字游戏\n") 270 | print(miyu) 271 | print("\n求解后的填字游戏\n") 272 | result = solve(miyu) 273 | print(result) 274 | 275 | #将解谜结果填到图片上 276 | for i in range(len(number_boxes)): 277 | if result[int(i/10)][i%10] != '0': 278 | x,y,w,h = number_boxes[99-i] 279 | # 第一个参数为打印的坐标,第二个为打印的文本,第三个为字体颜色,第四个为字体 280 | draw.text((x+10,y+10), str(result[int(i/10)][i%10]), (0, 0, 255), font=font) 281 | 282 | 283 | cv2_text_im = cv2.cvtColor(np.array(pil_im), cv2.COLOR_BGR2RGB) 284 | cv2.namedWindow("img", cv2.WINDOW_NORMAL) 285 | cv2.imshow("img", cv2_text_im) 286 | cv2.waitKey(0) 287 | cv2.destroyAllWindows() 288 | --------------------------------------------------------------------------------