干货 | 手把手教你用115行代码做个数独解析器!

发布时间:2025-10-03 点击:23
你也是数独爱好者吗?
aakash jhawar和许多人一样,乐于挑战新的难题。上学的时候,他每天早上都要玩数独。长大后,随着科技的进步,我们可以让计算机来帮我们解数独了!只需要点击数独的图片,它就会为你填满全部九宫格。
叮~ 这里有一份数独解析教程,等待你查收~ 喜欢收藏硬核干货的小伙伴看过来~
我们都知道,数独由9×9的格子组成,每行、列、宫各自都要填上1-9的数字,要做到每行、列、宫里的数字都不重复。
可以将解析数独的整个过程分成3步:
第一步:从图像中提取数独
第二步:提取图像中出现的每个数字
第三步:用算法计算数独的解
第一步:从图像中提取数独
首先需要进行图像处理。
1、对图像进行预处理
首先,我们应用高斯模糊的内核大小(高度,宽度)为9的图像。注意,内核大小必须是正的和奇数的,并且内核必须是平方的。然后使用11个最近邻像素自适应阈值。
proc=cv2.gaussianblur(img.copy(),(9,9),0)proc=cv2.adaptivethreshold(proc,255,cv2.adaptive_thresh_gaussian_c,cv2.thresh_binary,11,2)
为了使网格线具有非零像素值,我们颠倒颜色。此外,把图像放大,以增加网格线的大小。
proc=cv2.bitwise_not(proc,proc)kernel=np.array([[0。,1.,0.],[1.,1.,1.],[0.,1.,0.]],np.uint8)proc=cv2.dilate(proc,kernel)
阈值化后的数独图像
2、找出最大多边形的角
下一步是寻找图像中最大轮廓的4个角。所以需要找到所有的轮廓线,按面积降序排序,然后选择面积最大的那个。
_,contours,h=cv2.findcontours(img.copy(),cv2.retr_external,cv2.chain_approx_simple)contours=sorted(contours,key=cv2.contourarea,reverse=true)polygon=contours[0]
使用的操作符。带有max和min的itemgetter允许我们获得该点的索引。每个点都是有1个坐标的数组,然后[0]和[1]分别用于获取x和y。
右下角点具有最大的(x y)值;左上角有点最小(x y)值;左下角则具有最小的(x – y)值;右上角则具有最大的(x – y)值。
bottom_right,_=max(enumerate([pt[0][0] pt[0][1]forptinpolygon]),key=operator.itemgetter(1))top_left,_=min(enumerate([pt[0][0] pt[0][1]forptinpolygon]),key=operator.itemgetter(1))bottom_left,_=min(enumerate([pt[0][0]-pt[0][1]forptinpolygon]),key=operator.itemgetter(1))top_right,_=max(enumerate([pt[0][0]-pt[0][1]forptinpolygon]),key=operator.itemgetter(1))
现在我们有了4个点的坐标,然后需要使用索引返回4个点的数组。每个点都在自己的一个坐标数组中。
[polygon[top_left][0],polygon[top_right][0],polygon[bottom_right][0],polygon[bottom_left][0]]
最大多边形的四个角
3、裁剪和变形图像
有了数独的4个坐标后,我们需要剪裁和弯曲一个矩形部分,从一个图像变成一个类似大小的正方形。由左上、右上、右下和左下点描述的矩形。
注意:将数据类型显式设置为float32或‘getperspectivetransform’会引发错误。
top_left,top_right,bottom_right,bottom_left=crop_rect[0],crop_rect[1],crop_rect[2],crop_rect[3]src=np.array([top_left,top_right,bottom_right,bottom_left],dtype=float32)side=max([distance_between(bottom_right,top_right),distance_between(top_left,bottom_left),distance_between(bottom_right,bottom_left),distance_between(top_left,top_right)])
用计算长度的边来描述一个正方形,这是要转向的新视角。然后要做的是通过比较之前和之后的4个点来得到用于倾斜图像的变换矩阵。最后,再对原始图像进行变换。
dst=np.array([[0,0],[side-1,0],[side-1,side-1],[0,side-1]],dtype=float32)m=cv2.getperspectivetransform(src,dst)cv2.warpperspective(img,m,(int(side),int(side)))
裁剪和变形后的数独图像
4、从正方形图像中推断网格
从正方形图像推断出81个单元格。我们在这里交换 j 和 i ,这样矩形就被存储在从左到右读取的列表中,而不是自上而下。
squares=[]side=img.shape[:1]sideside=side[0]/9forjinrange(9):foriinrange(9):p1=(i*side,j*side)#topleftcornerofaboxp2=((i 1)*side,(j 1)*side)#bottomrightcornersquares.append((p1,p2))returnsquares
5、得到每一位数字
下一步是从其单元格中提取数字并构建一个数组。
digits=[]img=pre_process_image(img.copy(),skip_dilate=true)forsquareinsquares:digits.append(extract_digit(img,square,size))
extract_digit 是从一个数独方块中提取一个数字(如果有的话)的函数。它从整个方框中得到数字框,使用填充特征查找来获得框中间的最大特征,以期在边缘找到一个属于该数字的像素,用于定义中间的区域。接下来,需要缩放并填充数字,让适合用于机器学习的数字大小的平方。同时,我们必须忽略任何小的边框。
defextract_digit(img,rect,size):digit=cut_from_rect(img,rect)h,w=digit.shape[:2]margin=int(np.mean([h,w])/2.5)_,bbox,seed=find_largest_feature(digit,[margin,margin],[w-margin,h-margin])digit=cut_from_rect(digit,bbox)w=bbox[1][0]-bbox[0][0]h=bbox[1][1]-bbox[0][1]ifw>0andh>0and(w*h)>100andlen(digit)>0:returnscale_and_centre(digit,size,4)else:returnnp.zeros((size,size),np.uint8)
最后的数独的形象
现在,我们有了最终的数独预处理图像,下一个任务是提取图像中的每一位数字,并将其存储在一个矩阵中,然后通过某种算法计算出数独的解。
第二步:提取图像中出现的每个数字
对于数字识别,我们将在mnist数据集上训练神经网络,该数据集包含60000张0到9的数字图像。从导入所有库开始。
importnumpyimportcv2fromkeras.datasetsimportmnistfromkeras.modelsimportsequentialfromkeras.layersimportdensefromkeras.layersimportdropoutfromkeras.layersimportflattenfromkeras.layers.convolutionalimportconv2dfromkeras.layers.convolutionalimportmaxpooling2dfromkeras.utilsimportnp_utilsfromkerasimportbackendaskimportmatplotlib.pyplotasplt
需要修复随机种子以确保可重复性。
k.set_image_dim_ordering(th)seed=7numpy.random.seed(seed)(x_train,y_train),(x_test,y_

怎么新建网站在符合要求的同时突出个性
什么是云服务器的ecs
这家国内最大的通信设备公司 居然连五杂域名都不放过
网站主办者冲突请核实后再次报备这是怎么回事呢
服务器托管云服务器区别
什么是特色域名?特色域名具备哪些优点?
湖北服务器负载均衡品牌云主机
三维标志可以申请商标注册么