用Python和颜色矩,手把手教你识别不同面额的人民币(附完整代码)
用Python实现人民币面额智能识别:从颜色矩到SVM分类实战
在数字支付盛行的今天,纸币识别技术依然在自动售货机、ATM机和金融点钞设备中扮演着关键角色。本文将带您从零开始构建一个基于颜色特征的人民币面额识别系统,使用Python中的PIL、NumPy和Scikit-learn等库,通过计算RGB颜色矩特征并训练SVM分类器,实现对1元到100元纸币的自动识别。
1. 项目准备与环境搭建
1.1 数据集获取与预处理
我们使用的数据集包含1元、5元、10元、20元、50元和100元六种面额的人民币图像,每种面额采集40张图片(正反面各20张),共计240张。图像命名遵循"面额_序号.png"的格式,如"1_1.png"、"100_40.png"等。
import os from PIL import Image import numpy as np # 设置图像路径 dataset_path = "currency_images/" image_files = os.listdir(dataset_path) print(f"共加载{len(image_files)}张图片")1.2 必需库的安装
确保已安装以下Python库:
- Pillow (PIL):图像处理
- NumPy:数值计算
- scikit-learn:机器学习模型
- matplotlib(可选):可视化
pip install pillow numpy scikit-learn matplotlib2. 颜色矩特征工程
2.1 颜色矩理论基础
颜色矩是图像颜色分布的统计特征,包括:
- 一阶矩(均值):反映颜色通道的平均亮度
- 二阶矩(标准差):描述颜色分布的离散程度
- 三阶矩(偏度):表示颜色分布的不对称性
对于RGB图像的每个通道,我们计算这三阶矩,共得到9个特征。
2.2 特征提取实现
def calculate_color_moments(image_array): """计算RGB图像的颜色矩特征""" # 将像素值归一化到[0,1]范围 pixels = image_array / 255.0 # 计算各阶矩 mean = np.mean(pixels, axis=(0,1)) std = np.std(pixels, axis=(0,1)) # 三阶矩计算 skewness = np.mean((pixels - mean)**3, axis=(0,1)) ** (1/3) return np.concatenate([mean, std, skewness]) def extract_features(image_path): """从单张图像提取特征""" img = Image.open(image_path) img_array = np.array(img) return calculate_color_moments(img_array)3. 构建分类模型
3.1 数据准备与划分
from sklearn.model_selection import train_test_split # 初始化特征和标签数组 X = np.zeros((len(image_files), 9)) # 9个特征 y = np.zeros(len(image_files)) # 面额标签 # 提取特征和标签 for i, filename in enumerate(image_files): img_path = os.path.join(dataset_path, filename) X[i] = extract_features(img_path) y[i] = float(filename.split('_')[0]) # 从文件名提取面额 # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42)3.2 支持向量机(SVM)模型训练
SVM特别适合小样本、高维度的分类问题,是我们纸币识别的理想选择。
from sklearn.svm import SVC from sklearn.metrics import accuracy_score # 初始化SVM分类器 svm_model = SVC(kernel='rbf', C=10, gamma='scale', random_state=42) # 训练模型 svm_model.fit(X_train, y_train) # 评估模型 train_acc = svm_model.score(X_train, y_train) test_acc = svm_model.score(X_test, y_test) print(f"训练集准确率: {train_acc:.2%}") print(f"测试集准确率: {test_acc:.2%}")4. 模型优化与性能提升
4.1 特征重要性分析
通过分析各颜色矩特征的贡献度,我们可以优化特征选择:
import matplotlib.pyplot as plt # 特征名称 feature_names = ['R_mean', 'G_mean', 'B_mean', 'R_std', 'G_std', 'B_std', 'R_skew', 'G_skew', 'B_skew'] # 计算各特征在不同面额上的区分度 feature_importance = [] for i in range(9): # 使用方差作为区分度指标 importance = np.var(X[:, i]) feature_importance.append(importance) # 可视化 plt.figure(figsize=(10, 5)) plt.bar(feature_names, feature_importance) plt.title('Feature Importance Analysis') plt.xticks(rotation=45) plt.ylabel('Variance') plt.tight_layout() plt.show()4.2 模型参数调优
使用网格搜索寻找最优超参数:
from sklearn.model_selection import GridSearchCV # 定义参数网格 param_grid = { 'C': [0.1, 1, 10, 100], 'gamma': ['scale', 'auto', 0.1, 1], 'kernel': ['rbf', 'linear'] } # 网格搜索 grid_search = GridSearchCV(SVC(), param_grid, cv=5, verbose=2) grid_search.fit(X_train, y_train) # 最佳参数 print("最佳参数:", grid_search.best_params_) print("最佳分数:", grid_search.best_score_)5. 实际应用与扩展
5.1 构建端到端识别系统
将上述流程封装成可重用的类:
class CurrencyRecognizer: def __init__(self, model_path=None): if model_path: self.model = joblib.load(model_path) else: self.model = SVC(kernel='rbf', C=10, gamma='scale') def train(self, X_train, y_train): self.model.fit(X_train, y_train) def predict(self, image_path): features = extract_features(image_path) return self.model.predict([features])[0] def save_model(self, save_path): joblib.dump(self.model, save_path) # 使用示例 recognizer = CurrencyRecognizer() recognizer.train(X_train, y_train) prediction = recognizer.predict("test_images/50_1.png") print(f"预测面额: {prediction}元")5.2 处理实际场景中的挑战
在实际应用中,我们可能遇到以下问题及解决方案:
光照条件变化:
- 使用色彩恒常性算法进行预处理
- 增加数据增强(调整亮度、对比度)
纸币褶皱或破损:
- 引入局部特征提取方法
- 使用深度学习模型提高鲁棒性
多纸币同时识别:
- 结合目标检测技术(如YOLO)
- 实现纸币定位后再分类
# 示例:光照归一化处理 def normalize_illumination(image_array): """简单的光照归一化""" lab_img = cv2.cvtColor(image_array, cv2.COLOR_RGB2LAB) l, a, b = cv2.split(lab_img) clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8)) cl = clahe.apply(l) limg = cv2.merge((cl,a,b)) return cv2.cvtColor(limg, cv2.COLOR_LAB2RGB)6. 项目扩展方向
6.1 融合多特征提升准确率
除了颜色矩,可以结合以下特征:
- 纹理特征:LBP、HOG
- 形状特征:边缘检测、轮廓分析
- 局部特征:SIFT、SURF
from skimage.feature import local_binary_pattern def extract_lbp_features(image_array, P=8, R=1): """提取LBP纹理特征""" gray = np.mean(image_array, axis=2) # 转为灰度 lbp = local_binary_pattern(gray, P=P, R=R, method='uniform') hist, _ = np.histogram(lbp, bins=P*(P-1)+3, range=(0, P*(P-1)+2)) return hist / hist.sum() # 归一化6.2 深度学习替代方案
对于更复杂的识别任务,可以考虑CNN模型:
import tensorflow as tf from tensorflow.keras import layers, models def build_cnn_model(input_shape, num_classes): model = models.Sequential([ layers.Conv2D(32, (3,3), activation='relu', input_shape=input_shape), layers.MaxPooling2D((2,2)), layers.Conv2D(64, (3,3), activation='relu'), layers.MaxPooling2D((2,2)), layers.Flatten(), layers.Dense(64, activation='relu'), layers.Dense(num_classes, activation='softmax') ]) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) return model # 使用示例 # 需要先将图像调整为统一尺寸并构建适合CNN的输入在实际项目中,我发现颜色矩特征对于新版人民币的识别效果较好,因为不同面额的颜色差异明显。但对于旧版或严重磨损的纸币,可能需要结合更多特征或采用深度学习方法来提高识别率。
