这次我们来系统梳理OpenCV的核心知识体系。作为计算机视觉领域最基础且应用最广泛的库OpenCV涵盖了从图像处理到AI视觉的完整技术栈。无论你是刚入门的新手还是需要快速回顾核心概念的在职开发者这篇文章都能帮你建立清晰的OpenCV知识框架。OpenCV最值得关注的特点是它的跨平台性和功能完整性。从基础的图像读写、像素操作到高级的图像分割、目标检测、特征提取再到实际应用的人脸识别、边缘检测OpenCV提供了一站式的解决方案。更重要的是它支持CPU和GPU加速可以在各种硬件环境下运行从树莓派到服务器集群都能胜任。本文将带你从OpenCV环境配置开始逐步深入图像处理的核心算法最后通过多个实战案例展示如何将这些技术应用到实际项目中。我们会重点讲解每个知识点的原理、使用场景和代码实现确保你能真正掌握而不仅仅是了解。1. OpenCV核心能力速览能力项说明图像处理基础图像读写、像素操作、色彩空间转换、几何变换图像分割阈值分割、边缘检测、区域生长、分水岭算法目标检测传统方法HOG/SVM深度学习YOLO/SSD特征提取角点检测、SIFT、SURF、ORB特征描述子图像滤波均值滤波、高斯滤波、中值滤波、双边滤波人脸识别Haar级联检测、LBPH识别、深度学习模型硬件支持CPU优化、GPU加速CUDA、多平台部署语言支持Python、C、Java、JavaScript等多语言接口适用场景工业检测、安防监控、医疗影像、自动驾驶、AR/VR2. OpenCV适用场景与使用边界OpenCV最适合需要快速原型开发和实际部署的计算机视觉项目。在工业领域它可以用于产品质检、尺寸测量在安防领域支持人脸识别、车辆检测在医疗领域辅助医学影像分析在教育领域作为计算机视觉入门的最佳实践工具。但是OpenCV也有其使用边界。对于需要极高精度的复杂场景纯传统视觉方法可能不如深度学习模型对于实时性要求极高的应用需要结合硬件加速和算法优化对于商业级产品需要考虑算法专利和授权问题。特别需要注意的是涉及人脸识别、生物特征等敏感技术时必须严格遵守隐私保护法规确保数据采集和使用符合法律要求。在实际部署前务必进行充分的测试和效果验证。3. 环境准备与前置条件3.1 操作系统要求Windows 7/10/11推荐Windows 10Ubuntu 16.04 / CentOS 7macOS 10.14也支持嵌入式平台如树莓派、Jetson Nano等3.2 Python环境配置# 创建虚拟环境推荐 python -m venv opencv_env # Windows激活 opencv_env\Scripts\activate # Linux/macOS激活 source opencv_env/bin/activate # 安装基础依赖 pip install numpy matplotlib3.3 OpenCV安装方式# 基础版本CPU only pip install opencv-python # 完整版本包含contrib模块 pip install opencv-contrib-python # 如果需要GPU支持CUDA pip install opencv-python-headless3.4 验证安装import cv2 print(fOpenCV版本: {cv2.__version__}) # 检查CUDA支持如果有GPU print(fCUDA设备数: {cv2.cuda.getCudaEnabledDeviceCount()})4. 图像处理基础实战4.1 图像读取与显示import cv2 import numpy as np # 读取图像 img cv2.imread(test.jpg) print(f图像尺寸: {img.shape}) # (高度, 宽度, 通道数) # 显示图像 cv2.imshow(Original Image, img) cv2.waitKey(0) # 等待按键 cv2.destroyAllWindows() # 保存图像 cv2.imwrite(output.jpg, img)4.2 色彩空间转换# BGR转灰度图 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # BGR转HSV用于颜色识别 hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # BGR转RGB用于matplotlib显示 rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB)4.3 图像几何变换# 缩放 resized cv2.resize(img, (640, 480)) # 旋转 (h, w) img.shape[:2] center (w // 2, h // 2) matrix cv2.getRotationMatrix2D(center, 45, 1.0) # 旋转45度 rotated cv2.warpAffine(img, matrix, (w, h)) # 仿射变换 pts1 np.float32([[50,50], [200,50], [50,200]]) pts2 np.float32([[10,100], [200,50], [100,250]]) matrix cv2.getAffineTransform(pts1, pts2) affine cv2.warpAffine(img, matrix, (w, h))5. 图像滤波技术详解5.1 常用滤波算法对比滤波类型适用场景优点缺点均值滤波简单噪声去除计算速度快边缘模糊高斯滤波高斯噪声保持边缘较好计算量稍大中值滤波椒盐噪声有效去除孤立噪声点边缘可能失真双边滤波保边去噪保持边缘清晰计算复杂度高5.2 滤波代码实现# 均值滤波 blur cv2.blur(img, (5, 5)) # 高斯滤波 gaussian cv2.GaussianBlur(img, (5, 5), 0) # 中值滤波 median cv2.medianBlur(img, 5) # 双边滤波 bilateral cv2.bilateralFilter(img, 9, 75, 75) # 自定义卷积核 kernel np.ones((5, 5), np.float32) / 25 custom cv2.filter2D(img, -1, kernel)5.3 滤波效果评估import matplotlib.pyplot as plt # 显示滤波效果对比 plt.figure(figsize(12, 8)) plt.subplot(231), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) plt.title(Original), plt.axis(off) plt.subplot(232), plt.imshow(cv2.cvtColor(blur, cv2.COLOR_BGR2RGB)) plt.title(Mean Filter), plt.axis(off) plt.subplot(233), plt.imshow(cv2.cvtColor(gaussian, cv2.COLOR_BGR2RGB)) plt.title(Gaussian Filter), plt.axis(off) plt.subplot(234), plt.imshow(cv2.cvtColor(median, cv2.COLOR_BGR2RGB)) plt.title(Median Filter), plt.axis(off) plt.subplot(235), plt.imshow(cv2.cvtColor(bilateral, cv2.COLOR_BGR2RGB)) plt.title(Bilateral Filter), plt.axis(off) plt.tight_layout() plt.show()6. 边缘检测技术实战6.1 边缘检测算法原理边缘检测是图像处理中的重要任务用于识别图像中亮度明显变化的点。OpenCV提供了多种边缘检测算法Sobel算子一阶微分检测方向性边缘Laplacian算子二阶微分对噪声敏感但定位准确Canny算法多阶段算法效果最优的经典方法6.2 Canny边缘检测详解# Canny边缘检测步骤 def canny_edge_detection(image, low_threshold50, high_threshold150): # 1. 转灰度图 gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 2. 高斯模糊降噪 blurred cv2.GaussianBlur(gray, (5, 5), 0) # 3. Canny边缘检测 edges cv2.Canny(blurred, low_threshold, high_threshold) return edges # 测试不同阈值效果 edges_weak canny_edge_detection(img, 30, 90) edges_medium canny_edge_detection(img, 50, 150) edges_strong canny_edge_detection(img, 100, 200) # 显示结果对比 plt.figure(figsize(15, 5)) plt.subplot(131), plt.imshow(edges_weak, cmapgray) plt.title(Weak Edges (30, 90)), plt.axis(off) plt.subplot(132), plt.imshow(edges_medium, cmapgray) plt.title(Medium Edges (50, 150)), plt.axis(off) plt.subplot(133), plt.imshow(edges_strong, cmapgray) plt.title(Strong Edges (100, 200)), plt.axis(off) plt.tight_layout() plt.show()6.3 边缘检测在工业中的应用# 工业零件边缘检测示例 def industrial_edge_detection(image_path): # 读取图像 img cv2.imread(image_path) # 预处理转灰度、降噪、增强对比度 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blurred cv2.GaussianBlur(gray, (7, 7), 0) # 自适应阈值处理 thresh cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) # 边缘检测 edges cv2.Canny(thresh, 50, 150) # 查找轮廓 contours, _ cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 绘制轮廓 result img.copy() cv2.drawContours(result, contours, -1, (0, 255, 0), 2) return result # 使用示例 industrial_result industrial_edge_detection(industrial_part.jpg) cv2.imshow(Industrial Detection, industrial_result) cv2.waitKey(0) cv2.destroyAllWindows()7. 图像分割技术深入7.1 阈值分割方法阈值分割是最基础的图像分割技术OpenCV提供了多种阈值处理方法# 全局阈值分割 ret, thresh1 cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 自适应阈值分割 thresh2 cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2) # Otsus二值化 ret, thresh3 cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 显示不同阈值方法效果 titles [Original, Global Threshold, Adaptive Mean, Otsus] images [gray, thresh1, thresh2, thresh3] plt.figure(figsize(12, 8)) for i in range(4): plt.subplot(2, 2, i1) plt.imshow(images[i], gray) plt.title(titles[i]) plt.axis(off) plt.show()7.2 分水岭算法分水岭算法适用于接触物体的分割def watershed_segmentation(image): # 转灰度并二值化 gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) ret, thresh cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU) # 噪声去除 kernel np.ones((3, 3), np.uint8) opening cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations2) # 确定背景区域 sure_bg cv2.dilate(opening, kernel, iterations3) # 确定前景区域 dist_transform cv2.distanceTransform(opening, cv2.DIST_L2, 5) ret, sure_fg cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0) # 找到未知区域 sure_fg np.uint8(sure_fg) unknown cv2.subtract(sure_bg, sure_fg) # 标记连通域 ret, markers cv2.connectedComponents(sure_fg) markers markers 1 markers[unknown 255] 0 # 分水岭算法 markers cv2.watershed(image, markers) image[markers -1] [255, 0, 0] # 标记边界为红色 return image, markers # 使用分水岭算法 segmented_img, markers watershed_segmentation(img.copy()) cv2.imshow(Watershed Segmentation, segmented_img) cv2.waitKey(0)8. 特征提取与匹配8.1 关键点检测算法OpenCV支持多种特征检测算法# 初始化特征检测器 sift cv2.SIFT_create() surf cv2.xfeatures2d.SURF_create(400) # 需要contrib模块 orb cv2.ORB_create() # 检测关键点和描述子 keypoints_sift, descriptors_sift sift.detectAndCompute(gray, None) keypoints_orb, descriptors_orb orb.detectAndCompute(gray, None) # 绘制关键点 img_sift cv2.drawKeypoints(img, keypoints_sift, None, flagscv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) img_orb cv2.drawKeypoints(img, keypoints_orb, None, color(0, 255, 0), flags0) # 显示结果 plt.figure(figsize(12, 6)) plt.subplot(121), plt.imshow(cv2.cvtColor(img_sift, cv2.COLOR_BGR2RGB)) plt.title(fSIFT Keypoints: {len(keypoints_sift)}), plt.axis(off) plt.subplot(122), plt.imshow(cv2.cvtColor(img_orb, cv2.COLOR_BGR2RGB)) plt.title(fORB Keypoints: {len(keypoints_orb)}), plt.axis(off) plt.show()8.2 特征匹配实战def feature_matching(img1, img2): # 转灰度 gray1 cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) gray2 cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) # 使用ORB检测特征 orb cv2.ORB_create() kp1, des1 orb.detectAndCompute(gray1, None) kp2, des2 orb.detectAndCompute(gray2, None) # 特征匹配 bf cv2.BFMatcher(cv2.NORM_HAMMING, crossCheckTrue) matches bf.match(des1, des2) # 按距离排序 matches sorted(matches, keylambda x: x.distance) # 绘制匹配结果 result cv2.drawMatches(img1, kp1, img2, kp2, matches[:50], None, flags2) return result # 特征匹配示例 img1 cv2.imread(object1.jpg) img2 cv2.imread(object2.jpg) matching_result feature_matching(img1, img2) cv2.imshow(Feature Matching, matching_result) cv2.waitKey(0) cv2.destroyAllWindows()9. 目标检测技术实战9.1 传统目标检测方法# HOG特征 SVM分类器的人体检测 def pedestrian_detection(image): # 初始化HOG描述符 hog cv2.HOGDescriptor() hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector()) # 多尺度检测 boxes, weights hog.detectMultiScale(image, winStride(8, 8), padding(32, 32), scale1.05) # 绘制检测框 for (x, y, w, h) in boxes: cv2.rectangle(image, (x, y), (x w, y h), (0, 255, 0), 2) return image # 使用示例 pedestrian_img cv2.imread(street.jpg) result pedestrian_detection(pedestrian_img.copy()) cv2.imshow(Pedestrian Detection, result) cv2.waitKey(0)9.2 深度学习目标检测# YOLO目标检测实现 def yolo_detection(image_path, config_path, weights_path, classes_path): # 加载YOLO网络 net cv2.dnn.readNetFromDarknet(config_path, weights_path) # 使用GPU加速如果可用 net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA) net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA) # 读取类别名称 with open(classes_path, r) as f: classes [line.strip() for line in f.readlines()] # 读取图像 image cv2.imread(image_path) height, width image.shape[:2] # 构建blob blob cv2.dnn.blobFromImage(image, 1/255.0, (416, 416), swapRBTrue, cropFalse) net.setInput(blob) # 前向传播 outputs net.forward() # 处理检测结果 boxes, confidences, class_ids [], [], [] for output in outputs: for detection in output: scores detection[5:] class_id np.argmax(scores) confidence scores[class_id] if confidence 0.5: # 置信度阈值 center_x int(detection[0] * width) center_y int(detection[1] * height) w int(detection[2] * width) h int(detection[3] * height) x int(center_x - w/2) y int(center_y - h/2) boxes.append([x, y, w, h]) confidences.append(float(confidence)) class_ids.append(class_id) # 非极大值抑制 indices cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4) # 绘制检测框 if len(indices) 0: for i in indices.flatten(): x, y, w, h boxes[i] label f{classes[class_ids[i]]}: {confidences[i]:.2f} cv2.rectangle(image, (x, y), (x w, y h), (0, 255, 0), 2) cv2.putText(image, label, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) return image # 使用示例需要下载YOLO权重文件 # result yolo_detection(test.jpg, yolov3.cfg, yolov3.weights, coco.names)10. 人脸识别系统搭建10.1 人脸检测def face_detection(image): # 加载Haar级联分类器 face_cascade cv2.CascadeClassifier(cv2.data.haarcascades haarcascade_frontalface_default.xml) gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 人脸检测 faces face_cascade.detectMultiScale(gray, scaleFactor1.1, minNeighbors5, minSize(30, 30)) # 绘制检测框 for (x, y, w, h) in faces: cv2.rectangle(image, (x, y), (x w, y h), (255, 0, 0), 2) return image, len(faces) # 实时人脸检测 def realtime_face_detection(): cap cv2.VideoCapture(0) face_cascade cv2.CascadeClassifier(cv2.data.haarcascades haarcascade_frontalface_default.xml) while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale(gray, 1.1, 5) for (x, y, w, h) in faces: cv2.rectangle(frame, (x, y), (x w, y h), (255, 0, 0), 2) cv2.imshow(Real-time Face Detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() # 启动实时检测 # realtime_face_detection()10.2 人脸识别系统# 基于LBPH的人脸识别 class FaceRecognizer: def __init__(self): self.recognizer cv2.face.LBPHFaceRecognizer_create() self.face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml) self.labels {} self.current_id 0 def add_training_data(self, image_paths, label): if label not in self.labels: self.labels[label] self.current_id self.current_id 1 faces [] ids [] for image_path in image_paths: img cv2.imread(image_path) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) face_rects self.face_cascade.detectMultiScale(gray, scaleFactor1.1, minNeighbors5) for (x, y, w, h) in face_rects: face_roi gray[y:yh, x:xw] faces.append(face_roi) ids.append(self.labels[label]) return faces, ids def train(self, faces, ids): self.recognizer.train(faces, np.array(ids)) def predict(self, image): gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) faces self.face_cascade.detectMultiScale(gray, 1.1, 5) results [] for (x, y, w, h) in faces: face_roi gray[y:yh, x:xw] label_id, confidence self.recognizer.predict(face_roi) # 查找标签名称 label_name [name for name, id in self.labels.items() if id label_id][0] results.append({ label: label_name, confidence: confidence, bbox: (x, y, w, h) }) return results # 使用示例 recognizer FaceRecognizer() # 添加训练数据需要准备实际的人脸图片 # faces, ids recognizer.add_training_data([person1_1.jpg, person1_2.jpg], Alice) # recognizer.train(faces, ids) # 预测 # result recognizer.predict(test_image)11. 性能优化与最佳实践11.1 图像处理性能优化import time def performance_optimization_demo(): # 测试图像 img cv2.imread(large_image.jpg) # 1. 使用ROIRegion of Interest start_time time.time() roi img[100:300, 200:400] # 只处理感兴趣区域 processed_roi cv2.GaussianBlur(roi, (5, 5), 0) img[100:300, 200:400] processed_roi roi_time time.time() - start_time # 2. 图像金字塔降采样处理 start_time time.time() small cv2.pyrDown(img) # 降采样 processed_small cv2.GaussianBlur(small, (5, 5), 0) result cv2.pyrUp(processed_small) # 上采样 pyramid_time time.time() - start_time print(fROI处理时间: {roi_time:.4f}秒) print(f金字塔处理时间: {pyramid_time:.4f}秒) # 使用GPU加速如果可用 def gpu_acceleration(): # 检查CUDA支持 if cv2.cuda.getCudaEnabledDeviceCount() 0: print(CUDA设备可用启用GPU加速) # 将数据上传到GPU gpu_img cv2.cuda_GpuMat() gpu_img.upload(img) # 在GPU上执行操作 gpu_blur cv2.cuda.createGaussianFilter(cv2.CV_8UC3, cv2.CV_8UC3, (5, 5), 0) gpu_result gpu_blur.apply(gpu_img) # 下载回CPU result gpu_result.download() else: print(CUDA设备不可用使用CPU处理) result cv2.GaussianBlur(img, (5, 5), 0) return result11.2 内存管理最佳实践# 正确的内存管理 def efficient_image_processing(image_path): # 使用with语句确保资源释放 img cv2.imread(image_path) try: # 处理图像 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 及时释放不再需要的大内存对象 del img # 显式释放内存 # 继续处理 edges cv2.Canny(gray, 50, 150) return edges except Exception as e: print(f处理错误: {e}) return None # 批量处理优化 def batch_processing(image_paths, batch_size10): results [] for i in range(0, len(image_paths), batch_size): batch_paths image_paths[i:i batch_size] batch_results [] for path in batch_paths: try: result process_single_image(path) batch_results.append(result) except Exception as e: print(f处理 {path} 时出错: {e}) batch_results.append(None) results.extend(batch_results) # 强制垃圾回收 import gc gc.collect() return results12. 常见问题与解决方案12.1 安装与导入问题问题现象可能原因解决方案ModuleNotFoundError: No module named cv2OpenCV未安装或环境问题pip install opencv-python或检查Python环境导入成功但函数调用报错版本不兼容或模块缺失安装完整版pip install opencv-contrib-pythonCUDA相关函数报错GPU驱动或CUDA工具包问题检查CUDA安装或使用CPU版本12.2 运行时常见错误# 健壮的图像处理函数 def robust_image_processing(image_path): try: # 检查文件是否存在 if not os.path.exists(image_path): raise FileNotFoundError(f图像文件不存在: {image_path}) # 读取图像 img cv2.imread(image_path) if img is None: raise ValueError(无法读取图像文件可能格式不支持) # 检查图像尺寸 if img.size 0: raise ValueError(图像尺寸为0可能文件损坏) # 处理图像 result cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) return result except Exception as e: print(f图像处理错误: {e}) return None # 处理不同图像格式 def handle_different_formats(image_path): # 支持的图像格式 supported_formats [.jpg, .jpeg, .png, .bmp, .tiff] file_ext os.path.splitext(image_path)[1].lower() if file_ext not in supported_formats: print(f不支持的图像格式: {file_ext}) return None return cv2.imread(image_path)12.3 性能问题排查def performance_profiling(): import cProfile import pstats def processing_function(): img cv2.imread(test.jpg) for i in range(100): # 模拟复杂处理 blurred cv2.GaussianBlur(img, (5, 5), 0) edges cv2.Canny(blurred, 50, 150) # 性能分析 profiler cProfile.Profile() profiler.enable() processing_function() profiler.disable() stats pstats.Stats(profiler) stats.sort_stats(cumulative) stats.print_stats(10) # 显示前10个最耗时的函数 # 内存使用监控 def memory_usage_monitor(): import psutil import os process psutil.Process(os.getpid()) memory_before process.memory_info().rss / 1024 / 1024 # MB # 执行图像处理 img cv2.imread(large_image.jpg) result cv2.GaussianBlur(img, (15, 15), 0) memory_after process.memory_info().rss / 1024 / 1024 print(f内存使用: {memory_before:.2f} MB - {memory_after:.2f} MB) print(f内存增加: {memory_after - memory_before:.2f} MB)13. 实际项目应用案例13.1 文档扫描仪应用def document_scanner(image): # 转换为灰度图 gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 高斯模糊 blurred cv2.GaussianBlur(gray, (5, 5), 0) # 边缘检测 edged cv2.Canny(blurred, 75, 200) # 查找轮廓 contours, _ cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) contours sorted(contours, keycv2.contourArea, reverseTrue)[:5] # 寻找文档轮廓 screen_cnt None for contour in contours: peri cv2.arcLength(contour, True) approx cv2.approxPolyDP(contour, 0.02 * peri, True) if len(approx) 4: screen_cnt approx break if screen_cnt is not None: # 透视变换 warped four_point_transform(image, screen_cnt.reshape(4, 2)) return warped else: return image def four_point_transform(image, pts): # 获取输入坐标点 rect order_points(pts) (tl, tr, br, bl) rect # 计算新图像的宽度 widthA np.sqrt(((br[0] - bl[0]) ** 2) ((br[1] - bl[1]) ** 2)) widthB np.sqrt(((tr[0] - tl[0]) ** 2) ((tr[1] - tl[1]) ** 2)) maxWidth max(int(widthA), int(widthB)) # 计算新图像的高度 heightA np.sqrt(((tr[0] - br[0]) ** 2) ((tr[1] - br[1]) ** 2)) heightB np.sqrt(((tl[0] - bl[0]) ** 2) ((tl[1] - bl[1]) ** 2)) maxHeight max(int(heightA), int(heightB)) # 构造变换矩阵 dst np.array([ [0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtypefloat32) # 执行透视变换 M cv2.getPerspectiveTransform(rect, dst) warped cv2.warpPerspective(image, M, (maxWidth, maxHeight)) return warped def order_points(pts): # 初始化坐标点 rect np.zeros((4, 2), dtypefloat32) # 计算中心点 s pts.sum(axis1) rect[0] pts[np.argmin(s)] # 左上角 rect[2] pts[np.argmax(s)] # 右下角 # 计算差值 diff np.diff(pts, axis1) rect[1] pts[np.arg