import cv2
def location(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度化
ret, thresh = cv2.threshold(gray, 200, 255, 1) # 阈值处理
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5)) # 核结构
dilated = cv2.dilate(thresh, kernel) # 膨胀
# 获取轮廓
mode = cv2.RETR_TREE # 轮廓检测模式
method = cv2.CHAIN_APPROX_SIMPLE # 轮廓近似方法
contours, hierarchy = cv2.findContours(dilated, mode, method) # 提取轮廓
# ------------ 提取小单元格(9*9=81个) ---------------
boxHierarchy = [] # 小单元格的hierarchy信息
imgBox = img.copy() # 用于显示每一个单元格的轮廓
for i in range(len(hierarchy[0])): # 针对外轮廓
if hierarchy[0][i][3] == 0: # 判断:父轮廓是外轮廓的对象(小单元格)
boxHierarchy.append(hierarchy[0][i]) # 将该符合条件轮廓的hierarchy放入
imgBox = cv2.drawContours(imgBox.copy(), contours, i, (0, 0, 255)) # 绘制单元格轮廓
cv2.imshow("boxes", imgBox) # 显示每一个小单元格的轮廓,测试用
# ------------- 提取数字边框(定位数字) -----------------
numberBoxes = [] # 所有数字的轮廓
imgNum = img.copy() # 用于显示数字轮廓
for j in range(len(boxHierarchy)):
if boxHierarchy[j][2] != -1: # 符合条件的是包含数字的小方格
numberBox = contours[boxHierarchy[j][2]] # 小单元内数字轮廓
numberBoxes.append(numberBox)
x, y, w, h = cv2.boundingRect(numberBox) # 矩形包围框
# 绘制矩形边框
imgNum = cv2.rectangle(imgNum.copy(), (x - 1, y - 1), (x + w + 1, y + h + 1), (0, 0, 255), 2)
cv2.imshow("imgNum", imgNum) # 数字轮廓
return contours, numberBoxes # 返回所有轮廓、数字轮廓
# ================== 主程序 =====================
original = cv2.imread('x.jpg')
cv2.imshow("original", original)
contours, numberBoxes = location(original)
cv2.waitKey()
cv2.destroyAllWindows()
## 2.
import cv2
import glob
import numpy as np
# ================ 函数:训练模型 =================
def getModel():
cols = 15 # 控制调整后图像列数
rows = 20 # 控制调整图像后行数
s = cols * rows # 控制调整后图像尺寸
data = [] # 存储所有数字的所有图像
labels = [] # 存储所有数字的标签
for i in range(1, 10):
iTen = glob.glob(f'template/{str(i)}/*.*') # 某特定数字的所有图像的文件名
num = [] # 临时列表,每次循环用来存储某一个数字的所有图像
for number in iTen: # 逐个提取文件名
image = cv2.imread(number) # 逐个读取文件,放入image中
# 预处理:色彩空间变换
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 预处理:大小调整
image = cv2.resize(image, (cols, rows))
# ------------ 预处理:阈值处理 ----------------
ata = cv2.ADAPTIVE_THRESH_GAUSSIAN_C # 自适应方法adaptiveMethod
tb = cv2.THRESH_BINARY # threshType阈值处理方式
image = cv2.adaptiveThreshold(image, 255, ata, tb, 11, 2)
num.append(image) # 把当前图像值放入num中
data.append(num) # 把单个数字的所有图像放入data
labels.extend([i] * len(num)) # 为每个图像添加标签(数字)
data = np.array(data)
# step2: 划分数据集——划分为训练集和测试集
train_data = data[:, :70] # 每个数字的前 70 张图片用于训练
test_data = data[:, 70:] # 每个数字的后 10 张图片用于测试
# step3: 塑形
# 数据调整,将每个数字的尺寸由15*20调整为1*300(一行300个像素)
train_data = train_data.reshape(-1, s).astype(np.float32)
test_data = test_data.reshape(-1, s).astype(np.float32)
# step4: 打标签
train_labels = np.repeat(np.arange(1, 10), 70)[:, np.newaxis] # 每个数字的标签(每个数字有 70 张训练图像)
test_labels = np.repeat(np.arange(1, 10), 10)[:, np.newaxis] # 每个数字有 10 张测试图片
# 确保训练集和测试集大小一致
print(f"训练集大小: {train_data.shape[0]}, 测试集大小: {test_data.shape[0]}")
print(f"训练标签大小: {train_labels.shape[0]}, 测试标签大小: {test_labels.shape[0]}")
# step5: 使用KNN模块
# 核心代码:初始化、训练、预测
knn = cv2.ml.KNearest_create()
knn.train(train_data, cv2.ml.ROW_SAMPLE, train_labels)
ret, result, neighbours, dist = knn.findNearest(test_data, k=5)
# step6: 验证——通过测试集校验准确率
print(f"预测结果: {result}")
print(f"测试标签: {test_labels}")
# 修正匹配方式,确保形状一致
if result.shape == test_labels.shape:
matches = np.equal(result.astype(np.int32), test_labels) # 使用 np.equal 进行逐元素比较
else:
print("预测结果和测试标签形状不一致,调整数据集划分")
return None
correct = np.count_nonzero(matches)
accuracy = correct * 100.0 / result.size
print("当前使用KNN识别手写数字的准确率为:", accuracy)
# step7: 返回训练好的模型
return knn
# ================== 主程序 =====================
knn = getModel()
###3.
import cv2
import glob
import numpy as np
# ======= 函数:获取所有轮廓、数字的轮廓信息 ========
def location(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度化
ret, thresh = cv2.threshold(gray, 200, 255, 1) # 阈值处理
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5)) # 核结构
dilated = cv2.dilate(thresh, kernel) # 膨胀
# 获取轮廓
mode = cv2.RETR_TREE # 轮廓检测模式
method = cv2.CHAIN_APPROX_SIMPLE # 轮廓近似方法
contours, hierarchy = cv2.findContours(dilated, mode, method) # 提取轮廓
# ------------ 提取小单元格(9*9=81个) ---------------
boxHierarchy = [] # 小单元格的hierarchy信息
imgBox = img.copy() # 用于显示每一个单元格的轮廓
for i in range(len(hierarchy[0])): # 针对外轮廓
if hierarchy[0][i][3] == 0: # 判断:父轮廓是外轮廓的对象(小单元格)
boxHierarchy.append(hierarchy[0][i]) # 将该符合条件轮廓的hierarchy放入
imgBox = cv2.drawContours(imgBox.copy(), contours, i, (0, 0, 255)) # 绘制单元格轮廓
cv2.imshow("boxes", imgBox) # 显示每一个小单元格的轮廓,测试用
# ------------- 提取数字边框(定位数字) -----------------
numberBoxes = [] # 所有数字的轮廓
imgNum = img.copy() # 用于显示数字轮廓
for j in range(len(boxHierarchy)):
if boxHierarchy[j][2] != -1: # 符合条件的是包含数字的小方格
numberBox = contours[boxHierarchy[j][2]] # 小单元内数字轮廓
numberBoxes.append(numberBox) # 每个数字轮廓加入numberBoxes中
x, y, w, h = cv2.boundingRect(numberBox) # 矩形包围框
# 绘制矩形边框
imgNum = cv2.rectangle(imgNum.copy(), (x - 1, y - 1), (x + w + 1, y + h + 1), (0, 0, 255), 2)
cv2.imshow("imgNum", imgNum) # 数字轮廓
return contours, numberBoxes # 返回所有轮廓、数字轮廓
# ================ 函数:训练模型 =================
def getModel():
cols = 15 # 控制调整后图像列数
rows = 20 # 控制调整图像后行数
s = cols * rows # 控制调整后图像尺寸
xt = []
for i in range(1, 10):
iTen = glob.glob(f'template/{str(i)}/*.*') # 获取某特定数字的所有图像文件名