别再数钱了!用Python颜色矩+SVM,教你自动识别6种面额人民币(附240张图数据集处理技巧)
用Python颜色矩与SVM打造智能零钱识别系统
每次从口袋里掏出一把零钱时,你是否也厌烦了手动分类的繁琐?作为一名经常需要处理零钱的便利店店主,我曾经花费大量时间在清点现金上,直到发现了计算机视觉的魔力。本文将带你从零开始,构建一个能够自动识别1元到100元人民币的智能系统,整个过程不需要复杂的深度学习框架,仅用Python基础图像处理库和Scikit-learn就能实现。
1. 理解颜色矩:纸币识别的数学基础
颜色矩(Color Moments)是图像处理中一种简单却有效的特征提取方法,特别适合处理颜色分布有明显差异的图像分类任务。对于人民币识别来说,不同面额的纸币在颜色分布上具有显著差异,这正是颜色矩大显身手的地方。
颜色矩包含三个核心指标:
- 一阶颜色矩(均值):反映图像颜色的平均亮度
- 二阶颜色矩(标准差):描述颜色分布的离散程度
- 三阶颜色矩(偏度):表示颜色分布的不对称性
计算这三个指标时,我们需要分别处理图像的R、G、B三个通道,因此每张图像最终会得到9个特征值(3通道×3阶矩)。
import numpy as np from PIL import Image def calculate_color_moments(image_path): img = Image.open(image_path) img = img.resize((100, 100)) # 统一图像尺寸 r, g, b = img.split() # 分离RGB通道 def get_moments(channel): channel = np.array(channel, dtype=np.float32) mean = np.mean(channel) # 一阶矩 std = np.std(channel) # 二阶矩 skewness = np.mean((channel - mean)**3) ** (1/3) # 三阶矩 return [mean, std, skewness] r_moments = get_moments(r) g_moments = get_moments(g) b_moments = get_moments(b) return r_moments + g_moments + b_moments提示:三阶颜色矩的计算公式有多种变体,这里使用的是最常用的立方根形式,能有效避免数值过大导致的问题。
2. 构建人民币数据集:从原始图像到特征矩阵
原始数据集包含240张人民币图像,涵盖6种面额(1元、5元、10元、20元、50元、100元),每种面额包含正反面及不同角度的照片。这种多样性对于构建鲁棒的分类器至关重要。
2.1 数据预处理流程
图像读取与尺寸统一化:
- 使用Pillow库读取图像
- 将所有图像调整为相同尺寸(如100×100像素)
中心区域裁剪:
- 聚焦于纸币中心区域,减少背景干扰
- 代码实现:
def crop_center(image, crop_size=80): width, height = image.size left = (width - crop_size)/2 top = (height - crop_size)/2 right = (width + crop_size)/2 bottom = (height + crop_size)/2 return image.crop((left, top, right, bottom))- 特征提取与标签生成:
- 对每张图像计算9维颜色矩特征
- 从文件名中提取面额作为标签
2.2 数据集划分策略
对于240张的小型数据集,合理的划分方式直接影响模型性能:
| 划分方式 | 训练集数量 | 测试集数量 | 适用场景 |
|---|---|---|---|
| 随机划分 | 192 (80%) | 48 (20%) | 基础验证 |
| 分层抽样 | 保持各类比例 | 保持各类比例 | 类别不均衡时 |
| 按角度划分 | 特定角度训练 | 其他角度测试 | 测试模型泛化性 |
from sklearn.model_selection import train_test_split # 基础随机划分 X_train, X_test, y_train, y_test = train_test_split( features, labels, test_size=0.2, random_state=42) # 分层抽样划分 X_train, X_test, y_train, y_test = train_test_split( features, labels, test_size=0.2, stratify=labels, random_state=42)3. SVM模型构建与调优实战
支持向量机(SVM)在小样本分类任务中表现出色,特别适合我们的纸币识别场景。下面详细介绍如何构建和优化SVM分类器。
3.1 基础SVM模型搭建
from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline # 创建包含数据标准化和SVM的管道 svm_pipeline = make_pipeline( StandardScaler(), SVC(kernel='rbf', class_weight='balanced') ) # 模型训练 svm_pipeline.fit(X_train, y_train) # 评估性能 train_score = svm_pipeline.score(X_train, y_train) test_score = svm_pipeline.score(X_test, y_test) print(f"训练集准确率: {train_score:.2%}, 测试集准确率: {test_score:.2%}")3.2 关键参数解析与调优
SVM的核心参数及其对模型的影响:
核函数(kernel):
linear:线性核,适合线性可分数据rbf:高斯核,处理非线性问题poly:多项式核,可调节多项式次数
C参数:
- 控制误分类的惩罚力度
- 值越大,模型越复杂,可能过拟合
- 值越小,模型越简单,可能欠拟合
gamma参数(仅对rbf/poly核有效):
- 控制单个样本的影响范围
- 值越大,决策边界越复杂
使用网格搜索进行参数优化:
from sklearn.model_selection import GridSearchCV param_grid = { 'svc__C': [0.1, 1, 10, 100], 'svc__gamma': [0.001, 0.01, 0.1, 1], 'svc__kernel': ['rbf', 'poly', 'linear'] } grid_search = GridSearchCV( svm_pipeline, param_grid, cv=5, scoring='accuracy', n_jobs=-1) grid_search.fit(X_train, y_train) print("最佳参数组合:", grid_search.best_params_) print("最佳交叉验证分数:", grid_search.best_score_)4. 提升模型性能的进阶技巧
当基础模型的准确率不理想时,可以从以下几个方向进行优化:
4.1 特征工程扩展
除了颜色矩,可以引入更多特征类型:
纹理特征:
- 使用LBP(Local Binary Patterns)捕捉纸币纹理
- 计算灰度共生矩阵(GLCM)特征
形状特征:
- 边缘检测后计算轮廓特征
- 关键点检测与描述
颜色空间转换:
- 将RGB转换为HSV、Lab等颜色空间
- 在不同颜色空间中计算特征
from skimage.feature import local_binary_pattern def extract_lbp_features(image_path): img = Image.open(image_path).convert('L') # 转换为灰度图 img = np.array(img) radius = 3 n_points = 8 * radius lbp = local_binary_pattern(img, n_points, radius, method='uniform') hist, _ = np.histogram(lbp, bins=np.arange(0, n_points + 3)) return hist / hist.sum() # 归一化直方图4.2 数据增强策略
针对小数据集的常见增强方法:
几何变换:
- 小角度旋转(±5度)
- 轻微平移和缩放
颜色扰动:
- 亮度/对比度微调
- 添加轻微噪声
from torchvision import transforms transform = transforms.Compose([ transforms.RandomRotation(5), transforms.RandomAffine(0, translate=(0.05, 0.05)), transforms.ColorJitter(brightness=0.1, contrast=0.1), ])4.3 模型集成与融合
结合多个模型的优势:
投票集成:
- 训练多个不同核函数的SVM
- 采用多数投票决定最终分类
特征堆叠:
- 用不同特征集训练多个模型
- 将预测概率作为新特征输入元分类器
from sklearn.ensemble import VotingClassifier from sklearn.svm import SVC svm_rbf = SVC(kernel='rbf', probability=True) svm_poly = SVC(kernel='poly', probability=True) svm_linear = SVC(kernel='linear', probability=True) voting_clf = VotingClassifier( estimators=[ ('svm_rbf', svm_rbf), ('svm_poly', svm_poly), ('svm_linear', svm_linear)], voting='soft') voting_clf.fit(X_train, y_train)在实际项目中,我发现将颜色矩与LBP纹理特征结合,配合RBF核的SVM,能在人民币识别任务上达到约92%的准确率。对于光照变化较大的场景,建议在预处理阶段加入直方图均衡化,这能显著提升模型鲁棒性。
