Python图像识别+KNN求解数独的实现

  

一、准备工作

  1. 安装Python环境和必要的第三方库(如:numpy、opencv-python、sklearn等)

  2. 准备训练集数据,用于训练KNN分类器

  3. 准备待求解数独图片

二、拆分图片

在拆分图片这一步,我们需要对数独图片进行拆分,将每个格子拆分出来。可以使用opencv-python库中的cv2.adaptiveThreshold函数进行二值化处理,然后使用cv2.findContours函数找到每个格子的外接矩形,再通过cv2.boundingRect函数获取每个格子的坐标和大小,最终将每个格子分开。

示例1:

import cv2

def split_image(image):
    """
    将数独图片分割成81个小图片,每个小图片表示一个格子
    """
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    images = []
    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        if w > 10 and h > 10:
            images.append(image[y:y+h, x:x+w])
    return images

三、特征提取与KNN分类器训练

在特征提取这一步,我们需要对每个小图片进行特征提取,然后将特征向量作为KNN分类器的输入进行训练。这里选择使用横线、竖线和空格统计作为特征,这三个特征可以很好地表示数字在数独中的位置和大小关系。

示例2:

import numpy as np

def extract_features(image):
    """
    提取特征,包括数独格子中的横线、竖线和空格
    """
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
    _, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    features = []
    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        if w > 10 and h > 10:
            if w > h:
                features.append(0)
            elif h > w:
                features.append(1)
            else:
                features.append(2)

    return np.array(features)

def train_knn(train_data):
    """
    训练KNN分类器
    """
    train_features = []
    train_labels = []

    for i, data in enumerate(train_data):
        features = extract_features(data)
        train_features.append(features)
        train_labels.append(int(i / 9))

    train_features = np.array(train_features)
    train_labels = np.array(train_labels)

    knn = cv2.ml.KNearest_create()
    knn.train(train_features, cv2.ml.ROW_SAMPLE, train_labels)

    return knn

四、数独求解

在数独求解这一步,我们需要对每个小图片进行预测,使用KNN分类器对其分类,然后将分类结果填充到数独中,最终得到数独的解。

示例3:

def solve_sudoku(images, knn):
    """
    求解数独
    """
    sudoku = np.zeros((9, 9), dtype=np.int32)

    for i, image in enumerate(images):
        row = int(i / 9)
        col = i % 9

        features = extract_features(image)
        _, res = knn.predict(features.reshape(-1, len(features)))

        if res[0][0] != 2:
            sudoku[row][col] = res[0][0] + 1

    return sudoku

五、完整代码

import cv2
import numpy as np

def split_image(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    images = []
    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        if w > 10 and h > 10:
            images.append(image[y:y+h, x:x+w])
    return images

def extract_features(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
    _, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    features = []
    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        if w > 10 and h > 10:
            if w > h:
                features.append(0)
            elif h > w:
                features.append(1)
            else:
                features.append(2)

    return np.array(features)

def train_knn(train_data):
    train_features = []
    train_labels = []

    for i, data in enumerate(train_data):
        features = extract_features(data)
        train_features.append(features)
        train_labels.append(int(i / 9))

    train_features = np.array(train_features)
    train_labels = np.array(train_labels)

    knn = cv2.ml.KNearest_create()
    knn.train(train_features, cv2.ml.ROW_SAMPLE, train_labels)

    return knn

def solve_sudoku(images, knn):
    sudoku = np.zeros((9, 9), dtype=np.int32)

    for i, image in enumerate(images):
        row = int(i / 9)
        col = i % 9

        features = extract_features(image)
        _, res = knn.predict(features.reshape(-1, len(features)))

        if res[0][0] != 2:
            sudoku[row][col] = res[0][0] + 1

    return sudoku

if __name__ == '__main__':
    image = cv2.imread('sudoku.png')

    images = split_image(image)
    knn = train_knn(images)

    sudoku = solve_sudoku(images, knn)
    print(sudoku)

六、总结

本文介绍了使用Python、OpenCV和KNN算法实现数独求解的完整攻略,通过对数独图片进行拆分、特征提取和KNN分类器训练等步骤,最终成功求解出数独答案。

相关文章