python skimage安装_python图像处理标准库PIL | 第22期

本文介绍PIL(Python Imaging Library)的基本用法,适合需要进行图像处理但不想使用庞大库如OpenCV的开发者。PIL在Python3中由Pillow模块提供支持,是广泛应用于图像处理的标准库。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

5dd88600343195463e8990d3e8708dfb.png

使用python完成图像处理或者计算机视觉的任务时,常常需要一个封装好的图像读取和简单处理的库。很多朋友会使用opencv的python接口或者skimage等等模块,不过还有一个很不错的选择,那就是PIL(Python Image Library)。在python3中提供PIL功能的是Pillow模块,如果还没安装的话,可以直接使用pip指令安装。一些大型的机器学习库其实已经依赖了这个模块,所以在安装那些机器学习模块的时候就自动安装了Pillow。从目前一些著名的第三方库源码中可以看到,PIL的使用是非常广泛的,所以PIL也成为了事实上的python图像处理标准模块。如果需要在项目中使用图像,但是又不便于使用opencv这种庞大的库,那么PIL就是个非常不错的选择。本文根据Pillow的文档,简要介绍一些PIL中几个常用的操作接口,更多的功能可以直接查看文档。本文的所有代码都是在notebook下运行,使用的demo图像是图像处理领域的著名样本lena。

>>>

python图像处理标准库PIL | 第22期​mp.weixin.qq.com
48552d916e2e400477b4d4124783b1e3.png
# 环境要求:Python 3.8+, scikit-learn, OpenCV, TensorFlow/Keras, numpy, pandas, matplotlib # 安装命令:pip install scikit-learn opencv-python tensorflow numpy pandas matplotlib scikit-image xgboost import os import numpy as np import cv2 import tensorflow as tf from sklearn.model_selection import train_test_split, GridSearchCV, StratifiedKFold from sklearn.preprocessing import StandardScaler, LabelEncoder from sklearn.ensemble import RandomForestClassifier from sklearn.svm import SVC from sklearn.metrics import classification_report, confusion_matrix, accuracy_score from sklearn.pipeline import make_pipeline from sklearn.decomposition import PCA import matplotlib.pyplot as plt import seaborn as sns from tensorflow.keras.applications import MobileNetV2, EfficientNetB0 # 改用更先进的轻量级模型 from tensorflow.keras.models import Model from tensorflow.keras.layers import GlobalAveragePooling2D, Dense from tensorflow.keras.optimizers import Adam from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau from skimage.feature import graycomatrix, graycoprops, hog import joblib import xgboost as xgb from imblearn.over_sampling import SMOTE import application2 # 设置中文字体支持 plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'WenQuanYi Micro Hei'] plt.rcParams['axes.unicode_minus'] = False # ====================== # 1. 数据准备与预处理(增强版) # ====================== def load_and_preprocess_data(data_dir, styles, img_size=(224, 224), max_per_class=1000, augment=True): """ 加载并预处理图像数据,包含数据增强 :param data_dir: 数据集目录 :param styles: 艺术风格列表 :param img_size: 目标图像尺寸 :param max_per_class: 每个风格最多加载的图像数 :param augment: 是否应用数据增强 :return: 图像数据和标签 """ images = [] labels = [] # 数据增强函数 def augment_image(img): # 随机旋转 angle = np.random.randint(-20, 20) M = cv2.getRotationMatrix2D((img.shape[1] // 2, img.shape[0] // 2), angle, 1.0) rotated = cv2.warpAffine(img, M, (img.shape[1], img.shape[0])) # 随机裁剪 crop_size = int(min(img.shape[0], img.shape[1]) * 0.8) x = np.random.randint(0, img.shape[1] - crop_size) y = np.random.randint(0, img.shape[0] - crop_size) cropped = img[y:y + crop_size, x:x + crop_size] cropped = cv2.resize(cropped, img_size) # 随机翻转 if np.random.rand() > 0.5: flipped = cv2.flip(img, 1) else: flipped = img # 随机亮度调整 hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV) hsv = hsv.astype("float32") hsv[:, :, 2] = hsv[:, :, 2] * np.random.uniform(0.8, 1.2) hsv[:, :, 2] = np.clip(hsv[:, :, 2], 0, 255) brightness_adjusted = cv2.cvtColor(hsv.astype("uint8"), cv2.COLOR_HSV2RGB) return [rotated, cropped, flipped, brightness_adjusted] print("开始加载和预处理图像...") for style_idx, style in enumerate(styles): style_dir = os.path.join(data_dir, style) if not os.path.exists(style_dir): print(f"警告: {style_dir} 目录不存在,跳过") continue print(f"处理风格: {style}") count = 0 for img_name in os.listdir(style_dir): if max_per_class and count >= max_per_class: break img_path = os.path.join(style_dir, img_name) try: # 读取并调整大小 img = cv2.imread(img_path) if img is None: continue # 调整尺寸和颜色空间转换 img = cv2.resize(img, img_size) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 添加原始图像 images.append(img) labels.append(style_idx) count += 1 # 数据增强(引用[2]建议) if augment and count < max_per_class // 2: # 限制增强数量 augmented_images = augment_image(img) for aug_img in augmented_images: if count >= max_per_class: break images.append(aug_img) labels.append(style_idx) count += 1 except Exception as e: print(f"处理图像 {img_path} 时出错: {str(e)}") print(f"已加载 {count} 张 {style} 风格的图像") return np.array(images), np.array(labels) # 设置数据集路径和风格类别 DATA_DIR = application2.MY_DATA_DIR # 需替换为实际数据集路径 ART_STYLES = application2.MY_ART_STYLES # 示例风格 # 加载数据(每类最多加载200张图像,应用数据增强) images, labels = load_and_preprocess_data(DATA_DIR, ART_STYLES, max_per_class=100, augment=True) if len(images) == 0: print("错误: 未加载到任何图像数据,请检查路径和文件") exit() print(f"成功加载 {len(images)} 张图像,{len(ART_STYLES)} 种风格") # 检查类别平衡 label_counts = {style: np.sum(labels == i) for i, style in enumerate(ART_STYLES)} print("类别分布:") for style, count in label_counts.items(): print(f"{style}: {count} 张") # 划分训练集和测试集 (80%训练, 20%测试) X_train, X_test, y_train, y_test = train_test_split( images, labels, test_size=0.2, random_state=42, stratify=labels ) # ====================== # 2. 特征工程(优化版) # ====================== def extract_traditional_features(images): """ 提取传统图像特征(优化版) :param images: 图像数据 :return: 特征矩阵 """ traditional_features = [] print("提取传统图像特征...") for i, img in enumerate(images): if i % 50 == 0: # 每处理50张图像输出一次进度 print(f"处理进度: {i + 1}/{len(images)}") features = [] try: # 1. 颜色直方图 (RGB + HSV) for j in range(3): # RGB hist = cv2.calcHist([img], [j], None, [64], [0, 256]) cv2.normalize(hist, hist) features.extend(hist.flatten()) # HSV颜色空间 hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV) for j in range(3): # HSV hist = cv2.calcHist([hsv], [j], None, [64], [0, 256]) cv2.normalize(hist, hist) features.extend(hist.flatten()) # 2. 纹理特征 (灰度共生矩阵 + HOG) gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) # GLCM纹理特征 glcm = graycomatrix(gray, distances=[1, 3], angles=[0, np.pi / 4, np.pi / 2, 3 * np.pi / 4], levels=256, symmetric=True, normed=True) for prop in ['contrast', 'dissimilarity', 'homogeneity', 'energy', 'correlation']: prop_val = graycoprops(glcm, prop) features.extend(prop_val.flatten()) # HOG特征(引用[2]建议) hog_features = hog(gray, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(2, 2), block_norm='L2-Hys') features.extend(hog_features) except Exception as e: print(f"处理图像时出错: {str(e)},使用默认特征值") # 提供默认特征值 features.extend([0.0] * (64 * 6 + 20 + len(hog_features)) if 'hog_features' in locals() else 36) traditional_features.append(features) return np.array(traditional_features) def create_feature_extractor(model_name='MobileNetV2'): """ 创建深度特征提取器(使用更先进的轻量级模型) :param model_name: 模型名称 (MobileNetV2 或 EfficientNetB0) :return: 特征提取模型 """ input_shape = (224, 224, 3) if model_name == 'EfficientNetB0': base_model = EfficientNetB0( weights='imagenet', include_top=False, input_shape=input_shape ) else: # 默认使用MobileNetV2 base_model = MobileNetV2( weights='imagenet', include_top=False, input_shape=input_shape ) # 添加全局平均池化层 x = base_model.output x = GlobalAveragePooling2D()(x) # 创建特征提取模型 model = Model(inputs=base_model.input, outputs=x) # 冻结基础模型权重 for layer in base_model.layers: layer.trainable = False return model def extract_deep_features(images, model): """ 使用预训练模型提取深度特征 :param images: 图像数据 :param model: 特征提取模型 :return: 深度特征矩阵 """ print("提取深度特征...") # 预处理图像 if isinstance(model.layers[0].input_shape[0], tuple): # EfficientNet preprocessed_imgs = tf.keras.applications.efficientnet.preprocess_input( images.astype('float32')) else: # MobileNetV2 preprocessed_imgs = tf.keras.applications.mobilenet_v2.preprocess_input( images.astype('float32')) # 分批提取特征以节省内存 batch_size = 32 deep_features = [] for i in range(0, len(preprocessed_imgs), batch_size): batch = preprocessed_imgs[i:i + batch_size] batch_features = model.predict(batch, verbose=0) deep_features.append(batch_features) if i % (4 * batch_size) == 0: # 每处理4个批次输出一次进度 print(f"深度特征提取进度: {min(i + batch_size, len(preprocessed_imgs))}/{len(preprocessed_imgs)}") deep_features = np.vstack(deep_features) return deep_features # 创建特征提取器(引用[4]建议使用轻量模型) feature_extractor = create_feature_extractor('MobileNetV2') print("开始提取传统特征...") train_trad_features = extract_traditional_features(X_train) test_trad_features = extract_traditional_features(X_test) print("开始提取深度特征...") train_deep_features = extract_deep_features(X_train, feature_extractor) test_deep_features = extract_deep_features(X_test, feature_extractor) # 合并特征 print("合并传统特征和深度特征...") X_train_features = np.hstack([train_trad_features, train_deep_features]) X_test_features = np.hstack([test_trad_features, test_deep_features]) # 释放内存 del train_trad_features, test_trad_features, train_deep_features, test_deep_features # 特征降维(PCA) print("应用PCA降维...") pca = PCA(n_components=0.95, random_state=42) # 保留95%的方差 X_train_pca = pca.fit_transform(X_train_features) X_test_pca = pca.transform(X_test_features) print(f"降维后特征数量: {X_train_pca.shape[1]} (原始: {X_train_features.shape[1]})") # 处理类别不平衡(SMOTE) print("应用SMOTE处理类别不平衡...") smote = SMOTE(random_state=42) X_train_res, y_train_res = smote.fit_resample(X_train_pca, y_train) # 特征标准化 scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train_res) X_test_scaled = scaler.transform(X_test_pca) # ====================== # 3. 模型构建与训练(优化版) # ====================== def train_xgboost(X_train, y_train): """ 训练XGBoost模型(引用[1]建议的先进算法) """ print("训练XGBoost模型...") # 标签编码 le = LabelEncoder() y_train_encoded = le.fit_transform(y_train) # 定义参数网格 param_grid = { 'n_estimators': [100, 200], 'max_depth': [3, 5, 7], 'learning_rate': [0.01, 0.1], 'subsample': [0.8, 1.0], 'colsample_bytree': [0.8, 1.0] } # 创建XGBoost分类器 xgb_model = xgb.XGBClassifier( objective='multi:softmax', random_state=42, n_jobs=-1 ) # 使用分层K折交叉验证 skf = StratifiedKFold(n_splits=3, shuffle=True, random_state=42) # 网格搜索 grid_search = GridSearchCV( estimator=xgb_model, param_grid=param_grid, scoring='accuracy', cv=skf, n_jobs=-1, verbose=1 ) grid_search.fit(X_train, y_train_encoded) print(f"最佳参数: {grid_search.best_params_}") print(f"最佳准确率: {grid_search.best_score_:.4f}") return grid_search.best_estimator_, le def train_cnn_model(X_train, y_train, X_val, y_val, num_classes): """ 训练端到端的CNN模型(可选) """ print("训练端到端CNN模型...") # 创建模型 base_model = MobileNetV2( weights='imagenet', include_top=False, input_shape=(224, 224, 3) ) # 添加自定义层 x = base_model.output x = GlobalAveragePooling2D()(x) x = Dense(512, activation='relu')(x) x = Dense(256, activation='relu')(x) predictions = Dense(num_classes, activation='softmax')(x) model = Model(inputs=base_model.input, outputs=predictions) # 冻结基础模型层 for layer in base_model.layers: layer.trainable = False # 编译模型 model.compile( optimizer=Adam(learning_rate=0.0001), loss='sparse_categorical_crossentropy', metrics=['accuracy'] ) # 回调函数(引用[3]建议的早停机制) callbacks = [ EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True), ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=3, min_lr=1e-6) ] # 训练模型 history = model.fit( X_train, y_train, epochs=50, batch_size=32, validation_data=(X_val, y_val), callbacks=callbacks, verbose=1 ) # 绘制训练历史 plt.figure(figsize=(12, 5)) plt.subplot(1, 2, 1) plt.plot(history.history['accuracy'], label='训练准确率') plt.plot(history.history['val_accuracy'], label='验证准确率') plt.title('模型准确率') plt.ylabel('准确率') plt.xlabel('轮次') plt.legend() plt.subplot(1, 2, 2) plt.plot(history.history['loss'], label='训练损失') plt.plot(history.history['val_loss'], label='验证损失') plt.title('模型损失') plt.ylabel('损失') plt.xlabel('轮次') plt.legend() plt.savefig('cnn_training_history.png') plt.show() return model print("构建和训练分类模型...") # 训练XGBoost模型(主要模型) xgb_model, label_encoder = train_xgboost(X_train_scaled, y_train_res) # 训练端到端CNN模型(可选) # 注意:需要原始图像数据,且需要额外划分验证集 X_train_cnn, X_val_cnn, y_train_cnn, y_val_cnn = train_test_split( X_train, y_train, test_size=0.2, random_state=42, stratify=y_train ) cnn_model = train_cnn_model(X_train_cnn, y_train_cnn, X_val_cnn, y_val_cnn, len(ART_STYLES)) # ====================== # 4. 模型评估(增强版) # ====================== def evaluate_model(model, X_test, y_test, model_name, le=None): """ 评估模型性能并可视化结果 """ print(f"\n{model_name} 性能评估:") # 对于XGBoost需要标签编码 if le: y_test_encoded = le.transform(y_test) y_pred = model.predict(X_test) y_pred_labels = le.inverse_transform(y_pred) else: y_pred = model.predict(X_test) y_pred_labels = np.argmax(y_pred, axis=1) # 计算准确率 accuracy = accuracy_score(y_test, y_pred_labels) print(f"整体准确率: {accuracy:.4f}") # 打印分类报告 print(classification_report(y_test, y_pred_labels, target_names=ART_STYLES)) # 绘制混淆矩阵 cm = confusion_matrix(y_test, y_pred_labels) plt.figure(figsize=(10, 8)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=ART_STYLES, yticklabels=ART_STYLES) plt.title(f'{model_name} 混淆矩阵 (准确率: {accuracy:.2f})') plt.xlabel('预测标签') plt.ylabel('真实标签') plt.savefig(f'{model_name}_confusion_matrix.png') plt.show() return accuracy # 评估XGBoost模型 xgb_accuracy = evaluate_model(xgb_model, X_test_scaled, y_test, "XGBoost", le=label_encoder) # 评估CNN模型 cnn_accuracy = evaluate_model(cnn_model, X_test, y_test, "CNN模型") # ====================== # 5. 模型集成(可选) # ====================== def ensemble_predict(models, weights, X): """ 集成多个模型的预测结果 """ predictions = np.zeros((X.shape[0], len(ART_STYLES))) for model, weight in zip(models, weights): if isinstance(model, xgb.XGBClassifier): pred = model.predict_proba(X) else: pred = model.predict(X) predictions += weight * pred return np.argmax(predictions, axis=1) # 集成模型(权重根据准确率分配) models = [xgb_model, cnn_model] weights = [xgb_accuracy, cnn_accuracy] # 根据准确率分配权重 total_weight = sum(weights) normalized_weights = [w / total_weight for w in weights] # 准备CNN模型的测试数据(需要原始图像) X_test_cnn = np.array([cv2.resize(img, (224, 224)) for img in X_test]) X_test_cnn = tf.keras.applications.mobilenet_v2.preprocess_input(X_test_cnn.astype('float32')) # 集成预测 ensemble_pred = ensemble_predict(models, normalized_weights, [X_test_scaled, X_test_cnn]) # 评估集成模型 ensemble_accuracy = accuracy_score(y_test, ensemble_pred) print(f"\n集成模型性能评估:") print(f"整体准确率: {ensemble_accuracy:.4f}") print(classification_report(y_test, ensemble_pred, target_names=ART_STYLES)) # ====================== # 6. 可视化预测结果(增强版) # ====================== def visualize_predictions(models, weights, X_test, y_test, num_samples=5): """ 可视化模型预测结果(支持多模型) """ plt.figure(figsize=(15, 10)) indices = np.random.choice(range(len(X_test)), num_samples) # 准备CNN输入 X_test_cnn = np.array([cv2.resize(img, (224, 224)) for img in X_test]) X_test_cnn = tf.keras.applications.mobilenet_v2.preprocess_input(X_test_cnn.astype('float32')) for i, idx in enumerate(indices): # 获取图像和真实标签 image = X_test[idx] true_label = ART_STYLES[y_test[idx]] # 获取特征 trad_features = extract_traditional_features([image]) deep_features = extract_deep_features(np.expand_dims(image, axis=0), feature_extractor) features = np.hstack([trad_features, deep_features]) features_pca = pca.transform(features) features_scaled = scaler.transform(features_pca) # 获取各模型预测 model_probs = [] model_preds = [] # XGBoost预测 xgb_prob = xgb_model.predict_proba(features_scaled)[0] model_probs.append(xgb_prob) model_preds.append(ART_STYLES[np.argmax(xgb_prob)]) # CNN预测 cnn_input = np.expand_dims(X_test_cnn[idx], axis=0) cnn_prob = cnn_model.predict(cnn_input, verbose=0)[0] model_probs.append(cnn_prob) model_preds.append(ART_STYLES[np.argmax(cnn_prob)]) # 集成预测 ensemble_prob = weights[0] * xgb_prob + weights[1] * cnn_prob ensemble_pred = ART_STYLES[np.argmax(ensemble_prob)] # 绘制图像和预测结果 plt.subplot(2, num_samples, i + 1) plt.imshow(image) plt.title(f"真实: {true_label}\n集成预测: {ensemble_pred}") plt.axis('off') # 绘制概率分布 plt.subplot(2, num_samples, i + num_samples + 1) width = 0.35 x = np.arange(len(ART_STYLES)) plt.bar(x - width / 2, model_probs[0], width, label='XGBoost') plt.bar(x + width / 2, model_probs[1], width, label='CNN') # 标记最高概率 max_idx = np.argmax(ensemble_prob) plt.annotate(f"{ensemble_prob[max_idx]:.2f}", xy=(max_idx, ensemble_prob[max_idx]), xytext=(max_idx, ensemble_prob[max_idx] + 0.05), arrowprops=dict(facecolor='black', shrink=0.05)) plt.xticks(x, ART_STYLES, rotation=45) plt.xlabel('艺术风格') plt.ylabel('概率') plt.ylim(0, 1) plt.legend() plt.tight_layout() plt.savefig('ensemble_prediction_visualization.png') plt.show() # 可视化集成模型的预测 visualize_predictions(models, normalized_weights, X_test, y_test, num_samples=5) # ====================== # 7. 保存模型 # ====================== # 设置保存目录 save_dir = "saved_models" os.makedirs(save_dir, exist_ok=True) # 保存最佳模型 xgb_model_path = os.path.join(save_dir, 'art_style_xgb_classifier.pkl') cnn_model_path = os.path.join(save_dir, 'art_style_cnn_classifier.h5') scaler_path = os.path.join(save_dir, 'feature_scaler.pkl') pca_path = os.path.join(save_dir, 'pca_transformer.pkl') label_encoder_path = os.path.join(save_dir, 'label_encoder.pkl') joblib.dump(xgb_model, xgb_model_path) cnn_model.save(cnn_model_path) joblib.dump(scaler, scaler_path) joblib.dump(pca, pca_path) joblib.dump(label_encoder, label_encoder_path) print(f"XGBoost模型已保存为 {xgb_model_path}") print(f"CNN模型已保存为 {cnn_model_path}") print(f"特征标准化器已保存为 {scaler_path}") print(f"PCA转换器已保存为 {pca_path}") print(f"标签编码器已保存为 {label_encoder_path}") print("程序执行完成!")报错Traceback (most recent call last): File "D:\python_program\python_work2\train5.py", line 522, in <module> ensemble_pred = ensemble_predict(models, normalized_weights, [X_test_scaled, X_test_cnn]) File "D:\python_program\python_work2\train5.py", line 499, in ensemble_predict predictions = np.zeros((X.shape[0], len(ART_STYLES))) AttributeError: 'list' object has no attribute 'shape'帮我解决并给出完整代码
最新发布
06-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值