Python图像处理实战:从基础操作到智能分析项目开发

发布时间:2026/7/30 3:57:25
Python图像处理实战:从基础操作到智能分析项目开发 9 图像处理实战从基础操作到完整项目开发在数字图像处理领域无论是计算机视觉项目还是日常的图片编辑需求掌握图像处理的核心技术都至关重要。本文将通过完整的实战案例带你系统学习图像处理的全流程从环境搭建、基础操作到项目实战每个环节都提供可运行的代码示例和详细解释。1. 图像处理基础概念与环境准备1.1 什么是数字图像处理数字图像处理是指使用计算机算法对数字图像进行分析、处理和解释的技术。简单来说就是将图像转换为数字矩阵然后通过数学运算来改善图像质量、提取有用信息或实现特定视觉效果。数字图像由像素组成每个像素包含颜色信息。在灰度图像中每个像素用一个数值表示亮度在彩色图像中每个像素用三个数值RGB表示颜色分量。1.2 环境准备与工具选择对于图像处理项目我们推荐使用Python语言因为它拥有丰富的图像处理库和简洁的语法。以下是环境配置步骤安装必要的库pip install opencv-python pip install pillow pip install matplotlib pip install numpy pip install scikit-image验证安装import cv2 import numpy as np from PIL import Image import matplotlib.pyplot as plt print(fOpenCV版本: {cv2.__version__}) print(fNumPy版本: {np.__version__})推荐开发环境Python 3.8Jupyter Notebook用于实验和调试PyCharm或VS Code用于项目开发至少4GB内存处理大图像时需要更多内存2. 图像基础操作与IO处理2.1 图像读取与显示图像读取是处理的第一步不同的库提供了不同的读取方式import cv2 import matplotlib.pyplot as plt from PIL import Image import numpy as np # 使用OpenCV读取图像 def read_image_opencv(image_path): # 读取图像-1表示包含alpha通道0表示灰度1表示彩色 img cv2.imread(image_path, 1) # OpenCV默认使用BGR格式需要转换为RGB img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) return img_rgb # 使用PIL读取图像 def read_image_pil(image_path): img Image.open(image_path) # 转换为numpy数组 img_array np.array(img) return img_array # 图像显示函数 def display_images(images, titles, figsize(15, 5)): plt.figure(figsizefigsize) for i, (image, title) in enumerate(zip(images, titles)): plt.subplot(1, len(images), i1) plt.imshow(image) plt.title(title) plt.axis(off) plt.tight_layout() plt.show() # 示例使用 image_path sample.jpg # 替换为你的图像路径 img_opencv read_image_opencv(image_path) img_pil read_image_pil(image_path) print(f图像形状: {img_opencv.shape}) print(f数据类型: {img_opencv.dtype}) print(f像素值范围: {img_opencv.min()} - {img_opencv.max()})2.2 图像基本属性与信息提取了解图像的基本属性对于后续处理至关重要def analyze_image_properties(image_path): # 读取图像 img cv2.imread(image_path) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 基本属性 height, width, channels img.shape total_pixels height * width image_size img.nbytes # 字节大小 print(f图像尺寸: {width} x {height}) print(f通道数: {channels}) print(f总像素数: {total_pixels}) print(f图像大小: {image_size} 字节) print(f数据类型: {img.dtype}) # 统计信息 print(f红色通道统计: 均值{img_rgb[:,:,0].mean():.2f}, 标准差{img_rgb[:,:,0].std():.2f}) print(f绿色通道统计: 均值{img_rgb[:,:,1].mean():.2f}, 标准差{img_rgb[:,:,1].std():.2f}) print(f蓝色通道统计: 均值{img_rgb[:,:,2].mean():.2f}, 标准差{img_rgb[:,:,2].std():.2f}) return img_rgb # 创建测试图像如果无真实图像 def create_test_image(): # 创建一个512x512的测试图像 test_img np.zeros((512, 512, 3), dtypenp.uint8) # 添加彩色矩形 cv2.rectangle(test_img, (100, 100), (200, 200), (255, 0, 0), -1) # 红色 cv2.rectangle(test_img, (250, 100), (350, 200), (0, 255, 0), -1) # 绿色 cv2.rectangle(test_img, (400, 100), (500, 200), (0, 0, 255), -1) # 蓝色 # 添加文字 cv2.putText(test_img, OpenCV Test, (150, 300), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2) return test_img # 使用测试图像进行分析 test_image create_test_image() analyze_image_properties lambda x: None # 重新定义避免错误 print(测试图像分析:) print(f形状: {test_image.shape})3. 图像预处理技术3.1 图像尺寸调整与缩放图像缩放是常见的预处理操作需要注意保持宽高比def resize_image(image, new_size, keep_aspect_ratioTrue): 调整图像尺寸 Args: image: 输入图像 new_size: 新尺寸 (width, height) 或缩放比例 keep_aspect_ratio: 是否保持宽高比 h, w image.shape[:2] if isinstance(new_size, float): # 按比例缩放 new_w int(w * new_size) new_h int(h * new_size) resized cv2.resize(image, (new_w, new_h), interpolationcv2.INTER_LINEAR) elif isinstance(new_size, tuple) and keep_aspect_ratio: # 保持宽高比调整到指定大小 target_w, target_h new_size scale min(target_w/w, target_h/h) new_w int(w * scale) new_h int(h * scale) resized cv2.resize(image, (new_w, new_h), interpolationcv2.INTER_LINEAR) # 填充到目标尺寸 delta_w target_w - new_w delta_h target_h - new_h top, bottom delta_h//2, delta_h - delta_h//2 left, right delta_w//2, delta_w - delta_w//2 resized cv2.copyMakeBorder(resized, top, bottom, left, right, cv2.BORDER_CONSTANT, value[0, 0, 0]) else: # 直接调整到指定尺寸 resized cv2.resize(image, new_size, interpolationcv2.INTER_LINEAR) return resized # 测试不同的缩放方法 test_img create_test_image() # 1. 按比例缩放 scaled_50 resize_image(test_img, 0.5) scaled_150 resize_image(test_img, 1.5) # 2. 调整到固定尺寸保持宽高比 resized_300x300 resize_image(test_img, (300, 300), keep_aspect_ratioTrue) # 3. 直接调整可能变形 resized_direct resize_image(test_img, (300, 100), keep_aspect_ratioFalse) images [test_img, scaled_50, scaled_150, resized_300x300, resized_direct] titles [原图, 缩放50%, 缩放150%, 调整到300x300(保持比例), 直接调整300x100] display_images(images, titles, figsize(20, 4))3.2 图像色彩空间转换不同的色彩空间适用于不同的处理任务def color_space_conversions(image): 演示不同色彩空间的转换 # RGB to Grayscale gray cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) # RGB to HSV hsv cv2.cvtColor(image, cv2.COLOR_RGB2HSV) # RGB to LAB lab cv2.cvtColor(image, cv2.COLOR_RGB2LAB) # RGB to YCrCb ycrcb cv2.cvtColor(image, cv2.COLOR_RGB2YCrCb) return gray, hsv, lab, ycrcb def demonstrate_color_spaces(image): gray, hsv, lab, ycrcb color_space_conversions(image) # 显示各个通道 images [ image, # 原图 gray, # 灰度图 hsv[:,:,0], hsv[:,:,1], hsv[:,:,2], # HSV各通道 lab[:,:,0], lab[:,:,1], lab[:,:,2], # LAB各通道 ycrcb[:,:,0], ycrcb[:,:,1], ycrcb[:,:,2] # YCrCb各通道 ] titles [ 原图(RGB), 灰度图, H通道, S通道, V通道, L通道, A通道, B通道, Y通道, Cr通道, Cb通道 ] plt.figure(figsize(20, 10)) for i, (img, title) in enumerate(zip(images, titles)): plt.subplot(3, 4, i1) if len(img.shape) 2: # 灰度图 plt.imshow(img, cmapgray) else: plt.imshow(img) plt.title(title) plt.axis(off) plt.tight_layout() plt.show() # 演示色彩空间转换 demonstrate_color_spaces(test_img)4. 图像滤波与增强4.1 常用滤波技术图像滤波用于去噪、平滑、边缘检测等def apply_filters(image): 应用各种图像滤波器 # 高斯模糊 gaussian_blur cv2.GaussianBlur(image, (5, 5), 0) # 中值滤波 median_blur cv2.medianBlur(image, 5) # 双边滤波保持边缘 bilateral_blur cv2.bilateralFilter(image, 9, 75, 75) # 边缘检测 - Sobel gray cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) sobelx cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize5) sobely cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize5) sobel_combined np.sqrt(sobelx**2 sobely**2) # 边缘检测 - Canny edges cv2.Canny(gray, 100, 200) return gaussian_blur, median_blur, bilateral_blur, sobel_combined, edges def demonstrate_filters(image): filters apply_filters(image) images [image] list(filters) titles [ 原图, 高斯模糊, 中值滤波, 双边滤波, Sobel边缘检测, Canny边缘检测 ] display_images(images, titles, figsize(20, 10)) # 创建带噪声的测试图像 def create_noisy_image(): clean_img create_test_image() # 添加高斯噪声 mean 0 sigma 25 gaussian_noise np.random.normal(mean, sigma, clean_img.shape).astype(np.uint8) noisy_img cv2.add(clean_img, gaussian_noise) return clean_img, noisy_img clean, noisy create_noisy_image() demonstrate_filters(noisy)4.2 图像增强技术图像增强可以改善视觉效果或为后续处理做准备def image_enhancement_techniques(image): 图像增强技术演示 # 直方图均衡化灰度图 gray cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) hist_eq cv2.equalizeHist(gray) # CLAHE对比度受限的自适应直方图均衡化 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8,8)) clahe_applied clahe.apply(gray) # 伽马校正 gamma 1.5 inv_gamma 1.0 / gamma table np.array([((i / 255.0) ** inv_gamma) * 255 for i in np.arange(0, 256)]).astype(uint8) gamma_corrected cv2.LUT(image, table) # 对比度拉伸 alpha 1.5 # 对比度控制 beta 10 # 亮度控制 contrast_stretched cv2.convertScaleAbs(image, alphaalpha, betabeta) return gray, hist_eq, clahe_applied, gamma_corrected, contrast_stretched def demonstrate_enhancement(image): enhancements image_enhancement_techniques(image) images [image] list(enhancements) titles [ 原图, 灰度图, 直方图均衡化, CLAHE, 伽马校正(γ1.5), 对比度拉伸 ] # 灰度图单独显示 gray_images enhancements[:3] # 前三个是灰度图 gray_titles titles[1:4] plt.figure(figsize(15, 10)) # 显示彩色增强 plt.subplot(2, 3, 1) plt.imshow(images[0]) plt.title(titles[0]) plt.axis(off) for i, (img, title) in enumerate(zip(enhancements[3:], titles[4:])): plt.subplot(2, 3, i2) plt.imshow(img) plt.title(title) plt.axis(off) # 显示灰度增强 for i, (img, title) in enumerate(zip(gray_images, gray_titles)): plt.subplot(2, 3, i4) plt.imshow(img, cmapgray) plt.title(title) plt.axis(off) plt.tight_layout() plt.show() # 演示增强技术 demonstrate_enhancement(test_img)5. 图像分割与特征提取5.1 阈值分割技术图像分割是将图像分成有意义的区域的过程def thresholding_techniques(image): 各种阈值分割方法 gray cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) # 全局阈值 _, global_thresh cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # Otsus 阈值 _, otsu_thresh cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 自适应阈值 adaptive_thresh cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) return gray, global_thresh, otsu_thresh, adaptive_thresh def demonstrate_thresholding(image): thresholds thresholding_techniques(image) images list(thresholds) titles [ 灰度原图, 全局阈值(127), Otsus阈值, 自适应阈值 ] display_images(images, titles, figsize(15, 10)) # 创建适合阈值分割的测试图像 def create_threshold_test_image(): img np.zeros((300, 300), dtypenp.uint8) # 添加不同亮度的区域 cv2.rectangle(img, (50, 50), (100, 100), 100, -1) # 暗区域 cv2.rectangle(img, (150, 50), (200, 100), 150, -1) # 中等亮度 cv2.rectangle(img, (250, 50), (300, 100), 200, -1) # 亮区域 # 添加高斯噪声 noise np.random.normal(0, 25, img.shape).astype(np.uint8) noisy_img cv2.add(img, noise) return noisy_img threshold_test create_threshold_test_image() demonstrate_thresholding(threshold_test)5.2 轮廓检测与形状分析轮廓检测用于识别图像中的物体边界def contour_detection_analysis(image): 轮廓检测与形状分析 # 转换为灰度并二值化 gray cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) _, binary cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 查找轮廓 contours, hierarchy cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 在原图上绘制轮廓 contour_image image.copy() cv2.drawContours(contour_image, contours, -1, (0, 255, 0), 2) # 分析每个轮廓 contour_info [] for i, contour in enumerate(contours): # 计算轮廓面积 area cv2.contourArea(contour) # 计算轮廓周长 perimeter cv2.arcLength(contour, True) # 轮廓近似 epsilon 0.02 * perimeter approx cv2.approxPolyDP(contour, epsilon, True) # 边界矩形 x, y, w, h cv2.boundingRect(contour) contour_info.append({ index: i, area: area, perimeter: perimeter, approx_points: len(approx), bounding_rect: (x, y, w, h) }) return binary, contour_image, contours, contour_info def demonstrate_contours(image): binary, contour_img, contours, info contour_detection_analysis(image) # 创建分析结果图像 analysis_img image.copy() for i, data in enumerate(info): if data[area] 100: # 只显示面积较大的轮廓 x, y, w, h data[bounding_rect] # 绘制边界矩形 cv2.rectangle(analysis_img, (x, y), (xw, yh), (255, 0, 0), 2) # 添加文本信息 text fArea: {data[area]:.0f} cv2.putText(analysis_img, text, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1) images [image, binary, contour_img, analysis_img] titles [原图, 二值图, 轮廓检测, 形状分析] display_images(images, titles) # 打印轮廓信息 print(轮廓分析结果:) for data in info: if data[area] 100: print(f轮廓{data[index]}: 面积{data[area]:.1f}, 周长{data[perimeter]:.1f}, f近似点数{data[approx_points]}) # 创建包含多个形状的测试图像 def create_shape_test_image(): img np.zeros((400, 400, 3), dtypenp.uint8) # 绘制不同形状 cv2.rectangle(img, (50, 50), (150, 150), (255, 255, 255), -1) # 正方形 cv2.circle(img, (300, 100), 50, (255, 255, 255), -1) # 圆形 cv2.ellipse(img, (200, 250), (80, 40), 0, 0, 360, (255, 255, 255), -1) # 椭圆 # 绘制三角形 pts np.array([[350, 250], [320, 320], [380, 320]], np.int32) cv2.fillPoly(img, [pts], (255, 255, 255)) return img shape_test create_shape_test_image() demonstrate_contours(shape_test)6. 完整图像处理项目实战6.1 项目需求分析智能图像分析工具我们将开发一个完整的图像处理工具具备以下功能图像基本信息分析自动色彩增强物体检测与计数图像质量评估批量处理支持6.2 项目架构设计import os import json from datetime import datetime from typing import Dict, List, Tuple class ImageAnalyzer: 智能图像分析器主类 def __init__(self): self.supported_formats [.jpg, .jpeg, .png, .bmp, .tiff] self.analysis_results {} def load_image(self, image_path: str) - np.ndarray: 加载图像文件 if not os.path.exists(image_path): raise FileNotFoundError(f图像文件不存在: {image_path}) _, ext os.path.splitext(image_path) if ext.lower() not in self.supported_formats: raise ValueError(f不支持的图像格式: {ext}) image cv2.imread(image_path) if image is None: raise ValueError(无法读取图像文件) return cv2.cvtColor(image, cv2.COLOR_BGR2RGB) def analyze_basic_properties(self, image: np.ndarray) - Dict: 分析图像基本属性 height, width, channels image.shape return { dimensions: f{width} x {height}, channels: channels, total_pixels: width * height, file_size_estimate: image.nbytes, data_type: str(image.dtype), color_mean: [float(image[:,:,i].mean()) for i in range(channels)], color_std: [float(image[:,:,i].std()) for i in range(channels)] } def enhance_image(self, image: np.ndarray, method: str auto) - np.ndarray: 图像增强 if method auto: # 自动选择增强方法 if image.std() 30: # 低对比度图像 # 使用CLAHE增强对比度 lab cv2.cvtColor(image, cv2.COLOR_RGB2LAB) clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8,8)) lab[:,:,0] clahe.apply(lab[:,:,0]) enhanced cv2.cvtColor(lab, cv2.COLOR_LAB2RGB) else: # 轻度伽马校正 gamma 1.2 table np.array([((i / 255.0) ** (1.0/gamma)) * 255 for i in np.arange(0, 256)]).astype(uint8) enhanced cv2.LUT(image, table) elif method histogram_equalization: # 直方图均衡化 lab cv2.cvtColor(image, cv2.COLOR_RGB2LAB) lab[:,:,0] cv2.equalizeHist(lab[:,:,0]) enhanced cv2.cvtColor(lab, cv2.COLOR_LAB2RGB) else: enhanced image.copy() return enhanced def detect_objects(self, image: np.ndarray, min_area: int 100) - Dict: 简单物体检测 gray cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) blurred cv2.GaussianBlur(gray, (5, 5), 0) # 自适应阈值 binary cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) # 形态学操作去除噪声 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) binary cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) # 查找轮廓 contours, _ cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) objects [] result_image image.copy() for i, contour in enumerate(contours): area cv2.contourArea(contour) if area min_area: # 计算边界框 x, y, w, h cv2.boundingRect(contour) # 计算中心点 center_x x w // 2 center_y y h // 2 objects.append({ id: i, area: area, bounding_box: (x, y, w, h), center: (center_x, center_y) }) # 在图像上绘制 cv2.rectangle(result_image, (x, y), (xw, yh), (0, 255, 0), 2) cv2.putText(result_image, fObj{i}, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) return { object_count: len(objects), objects: objects, annotated_image: result_image } def assess_quality(self, image: np.ndarray) - Dict: 图像质量评估 gray cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) # 计算清晰度使用拉普拉斯方差 sharpness cv2.Laplacian(gray, cv2.CV_64F).var() # 计算对比度 contrast gray.std() # 计算亮度 brightness gray.mean() # 评估结果 quality_score (sharpness / 1000 contrast / 50 (128 - abs(brightness - 128)) / 128) / 3 return { sharpness: sharpness, contrast: contrast, brightness: brightness, quality_score: quality_score, quality_level: 优秀 if quality_score 0.7 else 良好 if quality_score 0.5 else 一般 } def comprehensive_analysis(self, image_path: str) - Dict: 综合图像分析 try: # 加载图像 image self.load_image(image_path) # 基本属性分析 basic_props self.analyze_basic_properties(image) # 图像增强 enhanced_image self.enhance_image(image) # 物体检测 object_detection self.detect_objects(image) # 质量评估 quality_assessment self.assess_quality(image) # 生成分析报告 analysis_report { timestamp: datetime.now().isoformat(), filename: os.path.basename(image_path), basic_properties: basic_props, quality_assessment: quality_assessment, object_detection: { count: object_detection[object_count], objects: object_detection[objects] }, enhancement_applied: True } # 保存结果图像 result_images { original: image, enhanced: enhanced_image, annotated: object_detection[annotated_image] } return { report: analysis_report, images: result_images, success: True } except Exception as e: return { success: False, error: str(e) }6.3 项目使用示例def demonstrate_image_analyzer(): 演示图像分析器的使用 # 创建分析器实例 analyzer ImageAnalyzer() # 使用测试图像 test_image create_test_image() # 保存测试图像 cv2.imwrite(test_analysis.jpg, cv2.cvtColor(test_image, cv2.COLOR_RGB2BGR)) # 进行分析 result analyzer.comprehensive_analysis(test_analysis.jpg) if result[success]: # 显示分析结果 report result[report] images result[images] print( 图像分析报告 ) print(f文件名: {report[filename]}) print(f分析时间: {report[timestamp]}) print(f图像尺寸: {report[basic_properties][dimensions]}) print(f检测到物体数量: {report[object_detection][count]}) print(f质量评估: {report[quality_assessment][quality_level]} f(得分: {report[quality_assessment][quality_score]:.2f})) # 显示图像对比 display_images( [images[original], images[enhanced], images[annotated]], [原图, 增强后, 物体检测], figsize(15, 5) ) # 显示详细统计信息 print(\n 详细统计 ) props report[basic_properties] print(f总像素: {props[total_pixels]}) print(f红色通道均值: {props[color_mean][0]:.1f} ± {props[color_std][0]:.1f}) print(f绿色通道均值: {props[color_mean][1]:.1f} ± {props[color_std][1]:.1f}) print(f蓝色通道均值: {props[color_mean][2]:.1f} ± {props[color_std][2]:.1f}) else: print(f分析失败: {result[error]}) # 清理临时文件 if os.path.exists(test_analysis.jpg): os.remove(test_analysis.jpg) # 运行演示 demonstrate_image_analyzer()7. 批量图像处理与性能优化7.1 批量处理实现在实际项目中经常需要处理大量图像class BatchImageProcessor: 批量图像处理器 def __init__(self, input_dir: str, output_dir: str): self.input_dir input_dir self.output_dir output_dir self.analyzer ImageAnalyzer() # 创建输出目录 os.makedirs(output_dir, exist_okTrue) def process_directory(self, enhancementTrue, detectionTrue, resizeNone, formatjpg) - Dict: 处理整个目录的图像 image_files self._find_image_files() results { processed: 0, failed: 0, details: [], total_time: 0 } start_time datetime.now() for filename in image_files: try: result self._process_single_file(filename, enhancement, detection, resize, format) results[details].append(result) results[processed] 1 except Exception as e: results[details].append({ filename: filename, success: False, error: str(e) }) results[failed] 1 results[total_time] (datetime.now() - start_time).total_seconds() return results def _find_image_files(self) - List[str]: 查找目录中的图像文件 supported self.analyzer.supported_formats image_files [] for file in os.listdir(self.input_dir): if any(file.lower().endswith(ext) for ext in supported): image_files.append(file) return sorted(image_files) def _process_single_file(self, filename: str, enhancement: bool, detection: bool, resize: Tuple, format: str) - Dict: 处理单个图像文件 input_path os.path.join(self.input_dir, filename) # 加载图像 image self.analyzer.load_image(input_path) # 应用处理 if enhancement: image self.analyzer.enhance_image(image) if resize: image resize_image(image, resize) # 物体检测可选 detection_result None if detection: detection_result self.analyzer.detect_objects(image) output_image detection_result[annotated_image] else: output_image image # 保存结果 output_filename fprocessed_{os.path.splitext(filename)[0]}.{format} output_path os.path.join(self.output_dir, output_filename) # 转换回BGR格式保存 output_bgr cv2.cvtColor(output_image, cv2.COLOR_RGB2BGR) cv2.imwrite(output_path, output_bgr) # 生成处理报告 basic_props self.analyzer.analyze_basic_properties(image) quality self.analyzer.assess_quality(image) return