paper 20 :color moments

本文探讨了颜色矩作为图像颜色特征表示的方法,包括一阶矩、二阶矩和三阶矩,阐述了其在表示图像颜色分布方面的优势及应用。通过程序实例展示了如何提取图像的颜色矩特征,并简要说明了该方法的局限性。

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

      图像的颜色特征是很重要的,其中颜色矩也是很重要的一部分。(还有一个关于图像颜色特征的review,对于image color写的比较全面)。还有,我要强调一下,本blog上的链接都是Google学术上的,要是进不去,需要用FQ软件的哦!

      颜色矩是一种简单有效的颜色特征表示方法,有一阶矩(均值,mean)、二阶矩(方差,viarance)和三阶矩(斜度,skewness)等,由于颜色信息主要分布于低阶矩中,所以用一阶矩,二阶矩和三阶矩足以表达图像的颜色分布,颜色矩已证明可有效地表示图像中的颜色分布,该方法的优点在于:不需要颜色空间量化,特征向量维数低;但实验发现该方法的检索效率比较低,因而在实际应用中往往用来过滤图像以缩小检索范围。

http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/AV0405/KEEN/av_as2_nkeen.pdf

color moments的定义都在上面的链接里。下面是我今天上午写的程序,欢迎指点,,,,

function [s]=colormoment(img1,img2)
% img1=imread('yuan.jpg');
R=img1(:,:,1);
G=img1(:,:,2);
B=img1(:,:,3);
% Extract Statistical features
% 1] MEAN
meanR=mean2(R);
meanG=mean2(G);
meanB=mean2(B);
% 2] Standard Deviation
stdR=std2(R);
stdG=std2(G);
stdB=std2(B);
% 3] Skewness
skeR=mean(skewness(double(R)));
skeG=mean(skewness(double(G)));
skeB=mean(skewness(double(B)));


A1=[meanR meanG meanB;stdR stdG stdB;skeR skeG skeB];
%%----------------------------------------------------------------

%%----------------------------------------------------------------
% img2=imread('test1.jpg');
R=img2(:,:,1);
G=img2(:,:,2);
B=img2(:,:,3);
% Extract Statistical features
% 1] MEAN
meanR=mean2(R);
meanG=mean2(G);
meanB=mean2(B);
% 2] Standard Deviation
stdR=std2(R);
stdG=std2(G);
stdB=std2(B);
% 3] Skewness
skeR=mean(skewness(double(R)));
skeG=mean(skewness(double(G)));
skeB=mean(skewness(double(B)));
A2=[meanR meanG meanB;stdR stdG stdB;skeR skeG skeB];
%%----------------------------------------------------------------
w=[1 2 1;1 2 1;1 2 1];
s=0;
for i=1:3
A=w(i,1).*abs(A1(i,1)-A2(i,1))+w(i,2).*abs(A1(i,2)-A2(i,2))+w(i,3).*abs(A1(i,3)-A2(i,3));
s=A+s;
end

    最后的部分有点乱,以后慢慢完善。时间比较紧缺,程序随手写,OK,不写了,将就看吧!

 

转载于:https://www.cnblogs.com/molakejin/p/5168628.html

import cv2 import numpy as np from matplotlib import pyplot as plt from sklearn.cluster import KMeans import math # 纸张尺寸标准 (单位: mm) PAPER_SIZES = { "A4": (210, 297), "B5": (176, 250), "A5": (148, 210) } #使用Harris角点检测算法检测图像中的角点""" def detect_corners_with_harris(image, block_size=2, ksize=3, k=0.04, threshold=0.01): if len(image.shape) == 3: gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) else: gray = image gray = np.float32(gray) dst = cv2.cornerHarris(gray, block_size, ksize, k) dst = cv2.dilate(dst, None) corner_img = image.copy() corners = [] threshold_val = threshold * dst.max() for i in range(dst.shape[0]): for j in range(dst.shape[1]): if dst[i, j] > threshold_val: corners.append((j, i)) cv2.circle(corner_img, (j, i), 5, (0, 0, 255), 2) return corner_img, corners #从Harris角点中找出最可能的纸张四个角点 def find_paper_corners_using_harris(corners, image_shape, top_k=4): height, width = image_shape[:2] top_left = (0, 0) top_right = (width, 0) bottom_right = (width, height) bottom_left = (0, height) candidates = { 'top_left': [], 'top_right': [], 'bottom_right': [], 'bottom_left': [] } for corner in corners: x, y = corner distances = { 'top_left': np.sqrt((x - top_left[0]) ** 2 + (y - top_left[1]) ** 2), 'top_right': np.sqrt((x - top_right[0]) ** 2 + (y - top_right[1]) ** 2), 'bottom_right': np.sqrt((x - bottom_right[0]) ** 2 + (y - bottom_right[1]) ** 2), 'bottom_left': np.sqrt((x - bottom_left[0]) ** 2 + (y - bottom_left[1]) ** 2) } closest_category = min(distances, key=distances.get) candidates[closest_category].append((corner, distances[closest_category])) selected_corners = [] for category in candidates: if candidates[category]: candidates[category].sort(key=lambda x: x[1]) selected_corners.append(candidates[category][0][0]) if len(selected_corners) < 4: return None return order_points(np.array(selected_corners)) #对点进行排序:左上,右上,右下,左下 def order_points(pts): rect = np.zeros((4, 2), dtype="float32") s = pts.sum(axis=1) rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] diff = np.diff(pts, axis=1) rect[1] = pts[np.argmin(diff)] rect[3] = pts[np.argmax(diff)] return rect #检测图像中的纸张轮廓 def detect_paper_contour(image): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (5, 5), 0) edged = cv2.Canny(blurred, 50, 200) contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5] for contour in contours: peri = cv2.arcLength(contour, True) approx = cv2.approxPolyDP(contour, 0.02 * peri, True) if len(approx) == 4: return order_points(approx.reshape(4, 2)), gray return None, gray #透视变换校正图像 def perspective_transform(image, points): def max_width_height(pts): width1 = np.sqrt(((pts[0][0] - pts[1][0]) ** 2) + ((pts[0][1] - pts[1][1]) ** 2)) width2 = np.sqrt(((pts[2][0] - pts[3][0]) ** 2) + ((pts[2][1] - pts[3][1]) ** 2)) max_width = max(int(width1), int(width2)) height1 = np.sqrt(((pts[0][0] - pts[3][0]) ** 2) + ((pts[0][1] - pts[3][1]) ** 2)) height2 = np.sqrt(((pts[1][0] - pts[2][0]) ** 2) + ((pts[1][1] - pts[2][1]) ** 2)) max_height = max(int(height1), int(height2)) return max_width, max_height points = order_points(points) max_width, max_height = max_width_height(points) dst = np.array([ [0, 0], [max_width - 1, 0], [max_width - 1, max_height - 1], [0, max_height - 1] ], dtype="float32") M = cv2.getPerspectiveTransform(points.astype("float32"), dst) warped = cv2.warpPerspective(image, M, (max_width, max_height)) return warped, M, max_width, max_height, points #创建纸张区域掩码 def create_paper_mask(width, height): mask = np.ones((height, width), dtype=np.uint8) * 255 print(f"创建纸张掩码,尺寸: {mask.shape}, 类型: {mask.dtype}") return mask #预处理图像以进行形状检测 def preprocess_image_for_shape_detection(image, paper_mask=None): # 转换为HSV颜色空间 hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 可选:如果有纸张掩码,仅处理纸张内部区域 if paper_mask is not None: # 确保掩码尺寸与图像匹配 if paper_mask.shape[:2] != hsv.shape[:2]: print(f"调整掩码尺寸: {paper_mask.shape} -> {hsv.shape[:2]}") paper_mask = cv2.resize(paper_mask, (hsv.shape[1], hsv.shape[0]), interpolation=cv2.INTER_NEAREST) # 确保掩码是uint8类型 if paper_mask.dtype != np.uint8: print("转换掩码数据类型为uint8") paper_mask = paper_mask.astype(np.uint8) # 应用掩码 try: hsv = cv2.bitwise_and(hsv, hsv, mask=paper_mask) except cv2.error as e: print(f"应用掩码时出错: {e}") print(f"HSV形状: {hsv.shape}, 掩码形状: {paper_mask.shape}") # 高斯模糊去噪 blurred = cv2.GaussianBlur(hsv, (5, 5), 0) return hsv, blurred #使用颜色聚类检测图像中的颜色区域 def detect_color_regions(image, paper_mask=None, n_clusters=6, min_area_ratio=0.002): h, w = image.shape[:2] # 计算最小面积阈值(基于纸张面积比例) min_area = min_area_ratio * w * h print(f"最小面积阈值: {min_area} 像素") # 调整图像大小以提高聚类效率 if h > 500 or w > 500: scale = 500 / max(h, w) resized = cv2.resize(image, None, fx=scale, fy=scale) h_resized, w_resized = resized.shape[:2] print(f"调整图像大小: {h}x{w} -> {h_resized}x{w_resized}, 缩放比例: {scale}") else: resized = image.copy() scale = 1.0 h_resized, w_resized = h, w print(f"图像大小未调整: {h}x{w}") # 预处理图像 hsv, blurred = preprocess_image_for_shape_detection(resized, paper_mask) # 转换为2D数组 pixel_values = hsv.reshape((-1, 3)) pixel_values = np.float32(pixel_values) print(f"像素值数组形状: {pixel_values.shape}") # 排除背景颜色(白色背景:低饱和度、高亮度) not_background = np.logical_and( hsv[:, :, 1] > 30, # 饱和度高(目标颜色) hsv[:, :, 2] < 230 # 亮度不极端(背景很亮) ) if paper_mask is not None: # 调整掩码尺寸以匹配调整后的图像 paper_mask_resized = cv2.resize(paper_mask, (w_resized, h_resized), interpolation=cv2.INTER_NEAREST) not_background = np.logical_and(not_background, paper_mask_resized > 0) pixel_mask = not_background.reshape(-1) valid_pixels = pixel_values[pixel_mask] print(f"有效像素数量: {len(valid_pixels)}") # 执行KMeans聚类 regions = [] if len(valid_pixels) > 0: # 显式设置n_init参数以避免FutureWarning kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10) kmeans.fit(valid_pixels) print(f"KMeans聚类完成,聚类数: {n_clusters}") # 获取聚类标签并转换回原始图像尺寸 labels = kmeans.predict(pixel_values) labels = labels.reshape((h_resized, w_resized)) print(f"标签数组形状: {labels.shape}") # 为每个聚类创建掩码 for cluster_id in range(n_clusters): mask = np.uint8(labels == cluster_id) * 255 # 形态学操作优化掩码 kernel = np.ones((5, 5), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2) # 开运算去噪 mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=2) # 闭运算连接区域 # 如果调整了图像大小,将掩码调整回原始尺寸 if scale != 1.0: mask = cv2.resize(mask, (w, h), interpolation=cv2.INTER_NEAREST) print(f"调整掩码尺寸: {mask.shape}") # 如果有纸张掩码,仅保留纸张内部区域 if paper_mask is not None: # 确保掩码尺寸匹配 if mask.shape[:2] != paper_mask.shape[:2]: paper_mask_resized = cv2.resize(paper_mask, (mask.shape[1], mask.shape[0]), interpolation=cv2.INTER_NEAREST) else: paper_mask_resized = paper_mask mask = cv2.bitwise_and(mask, paper_mask_resized) # 查找轮廓 contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) print(f"聚类 {cluster_id} 检测到 {len(contours)} 个轮廓") for contour in contours: area = cv2.contourArea(contour) if area > min_area: # 过滤小区域 regions.append(contour) print(f" 保留轮廓,面积: {area} 像素") print(f"总共检测到 {len(regions)} 个颜色区域") return regions #识别物体形状,优化近似参数和判断逻辑 def identify_shape(contour): peri = cv2.arcLength(contour, True) if peri == 0: return "不规则形状" # 更严格的多边形近似(epsilon=0.02*peri) approx = cv2.approxPolyDP(contour, 0.02 * peri, True) num_sides = len(approx) area = cv2.contourArea(contour) circularity = 4 * np.pi * area / (peri ** 2) # 椭圆拟合(增加轮廓点数判断,避免报错) if len(contour) >= 5: try: ellipse = cv2.fitEllipse(contour) (center, axes, orientation) = ellipse major_axis = max(axes) minor_axis = min(axes) eccentricity = np.sqrt(1 - (minor_axis / major_axis) ** 2) if major_axis != 0 else 0 ellipse_area = np.pi * (major_axis / 2) * (minor_axis / 2) ellipse_ratio = area / ellipse_area if ellipse_area > 0 else 0 # 更严格的椭圆判定条件 if 0.8 <= ellipse_ratio <= 1.2 and 0.5 < eccentricity < 0.95: return "椭圆" except: pass # 多边形形状判断(优化长宽比计算) if num_sides == 3: return "三角形" elif num_sides == 4: rect = cv2.minAreaRect(contour) w, h = rect[1] if min(w, h) == 0: # 避免除以0 aspect_ratio = 0 else: aspect_ratio = max(w, h) / min(w, h) if 0.85 <= aspect_ratio <= 1.15: return "正方形" else: return "矩形" elif circularity > 0.85: # 圆形判定(圆度更高) return "圆形" elif 5 <= num_sides <= 10: return f"{num_sides}边形" else: return "不规则形状" #计算物体的几何属性 def calculate_properties(contour, pixel_to_mm=1.0): rect = cv2.minAreaRect(contour) width_px, height_px = rect[1] dimensions_px = sorted([width_px, height_px], reverse=True) length_px, width_px = dimensions_px length_mm = length_px * pixel_to_mm width_mm = width_px * pixel_to_mm area_px = cv2.contourArea(contour) area_mm = area_px * (pixel_to_mm ** 2) M = cv2.moments(contour) if M["m00"] != 0: cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) else: cx, cy = 0, 0 return length_mm, width_mm, area_mm, (cx, cy), length_px, width_px, area_px #提取物体主要颜色(HSV) def extract_dominant_color(image, contour): mask = np.zeros(image.shape[:2], np.uint8) cv2.drawContours(mask, [contour], -1, 255, -1) hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 提取掩码区域的颜色 masked_pixels = hsv[mask == 255] if len(masked_pixels) == 0: return (0, 0, 0) # 计算主导颜色 (使用中位数更鲁棒) dominant_hsv = np.median(masked_pixels, axis=0) return tuple(map(int, dominant_hsv)) #可视化检测过程的各个步骤 def visualize_detection_steps(image, corrected_image, paper_mask, color_regions, result_img): """可视化检测过程的各个步骤,每个步骤显示在单独的窗口中""" # 显示原始图像 plt.figure(figsize=(10, 8)) plt.title('original image') plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) plt.axis('off') plt.tight_layout() plt.show() # 显示校正后的图像 plt.figure(figsize=(10, 8)) plt.title('corrected image') plt.imshow(cv2.cvtColor(corrected_image, cv2.COLOR_BGR2RGB)) plt.axis('off') plt.tight_layout() plt.show() # 显示颜色聚类区域 color_clusters = corrected_image.copy() colors = [(0, 0, 255), (0, 255, 0), (255, 0, 0), (255, 255, 0), (0, 255, 255), (255, 0, 255), (128, 0, 0), (0, 128, 0)] for i, contour in enumerate(color_regions): color = colors[i % len(colors)] cv2.drawContours(color_clusters, [contour.astype(int)], -1, color, 3) plt.figure(figsize=(10, 8)) plt.title('Color Cluster Area') plt.imshow(cv2.cvtColor(color_clusters, cv2.COLOR_BGR2RGB)) plt.axis('off') plt.tight_layout() plt.show() # 显示二值化结果 gray = cv2.cvtColor(corrected_image, cv2.COLOR_BGR2GRAY) binary = cv2.adaptiveThreshold( gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2 ) if paper_mask is not None: binary = cv2.bitwise_and(binary, binary, mask=paper_mask) plt.figure(figsize=(10, 8)) plt.title('Binary image') plt.imshow(binary, cmap='gray') plt.axis('off') plt.tight_layout() plt.show() # 显示最终识别结果 plt.figure(figsize=(10, 8)) plt.title('Shape recognition results') plt.imshow(cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)) plt.axis('off') plt.tight_layout() plt.show() #主函数 def main(image_path): image = cv2.imread(image_path) if image is None: print(f"错误:无法读取图像 {image_path}") return # 1. 使用Harris角点检测纸张边框 corner_img, corners = detect_corners_with_harris(image) paper_points = find_paper_corners_using_harris(corners, image.shape) # 初始化变量 corrected_image = image.copy() paper_mask = None pixel_to_mm = 1.0 paper_detected = False # 2. 透视变换校正图像 if paper_points is not None: # 执行透视变换 corrected_image, M, max_width, max_height, original_points = perspective_transform(image, paper_points) paper_detected = True print("成功检测并校正纸张轮廓") # 创建纸张掩码(仅处理纸张内部区域) paper_mask = create_paper_mask(max_width, max_height) # 计算物理尺寸转换因子 paper_width_mm, paper_height_mm = PAPER_SIZES["A4"] pixel_to_mm_x = paper_width_mm / max_width pixel_to_mm_y = paper_height_mm / max_height pixel_to_mm = (pixel_to_mm_x + pixel_to_mm_y) / 2.0 else: print("未检测到纸张轮廓 - 使用原始图像进行分析") h, w = image.shape[:2] paper_mask = create_paper_mask(w, h) # 3. 基于颜色聚类和形态学操作检测几何形状 color_regions = detect_color_regions( corrected_image, paper_mask=paper_mask, n_clusters=6, # 调整为6个聚类,匹配目标颜色数量 min_area_ratio=0.002 # 提高最小面积阈值,过滤小区域 ) if not color_regions: print("未检测到颜色区域 - 尝试使用二值化方法检测") # 备用方案:使用二值化方法检测 gray = cv2.cvtColor(corrected_image, cv2.COLOR_BGR2GRAY) binary = cv2.adaptiveThreshold( gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2 ) if paper_mask is not None: binary = cv2.bitwise_and(binary, binary, mask=paper_mask) # 形态学操作 kernel = np.ones((5, 5), np.uint8) binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=2) binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=2) # 查找轮廓 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) min_area = 0.001 * binary.shape[0] * binary.shape[1] color_regions = [c for c in contours if cv2.contourArea(c) > min_area] if not color_regions: print("未检测到几何图形") return # 初始化结果列表 results = [] result_img = corrected_image.copy() # 处理每个颜色区域 for i, contour in enumerate(color_regions): # 提取主导颜色 hsv_color = extract_dominant_color(corrected_image, contour) rgb_color = cv2.cvtColor(np.uint8([[hsv_color]]), cv2.COLOR_HSV2BGR)[0][0] color_name = f"RGB({rgb_color[2]}, {rgb_color[1]}, {rgb_color[0]})" # 识别形状 shape = identify_shape(contour) # 计算几何属性 length_mm, width_mm, area_mm, center_px, length_px, width_px, area_px = calculate_properties( contour, pixel_to_mm ) # 存储结果 results.append({ "id": i + 1, "shape": shape, "length_mm": length_mm, "width_mm": width_mm, "area_mm": area_mm, "color": color_name, "hsv_color": hsv_color, "center_px": center_px, "length_px": length_px, "width_px": width_px, "area_px": area_px }) # 在图像上标注物体 color = (int(rgb_color[2]), int(rgb_color[1]), int(rgb_color[0])) cv2.drawContours(result_img, [contour], -1, color, 3) cv2.putText(result_img, f"{i + 1}({shape})", (int(center_px[0]), int(center_px[1])), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2) # 可视化检测步骤 visualize_detection_steps(image, corrected_image, paper_mask, color_regions, result_img) # 打印分析结果 print("\n===== 图像分析结果 =====") print(f"检测到 {len(results)} 个几何图形") print("-" * 70) for obj in results: print(f"图形 {obj['id']}:") print(f" 形状: {obj['shape']}") print(f" 长度: {obj['length_mm']:.2f} mm ({obj['length_px']:.1f} 像素)") print(f" 宽度: {obj['width_mm']:.2f} mm ({obj['width_px']:.1f} 像素)") print(f" 面积: {obj['area_mm']:.2f} mm² ({obj['area_px']:.1f} 像素²)") print(f" 颜色: {obj['color']} (HSV: {obj['hsv_color']})") print(f" 中心坐标: ({obj['center_px'][0]}, {obj['center_px'][1]})") print("-" * 70) if __name__ == "__main__": image_path = "ALL2.jpg" # 替换为实际图像路径 main(image_path) 上述代码创建纸张区域掩码后要对图像进行处理再进一步识别图像内部的不同几何形状,上述代码所采用的是提取颜色进行识别,但是对于图片中的颜色可能是分配不均匀的,所以会导致识别效果不是很好,我想换成边缘检测的方法识别。先对图片进行二值化操作再进行边缘检测。请根据我的描述修改上述代码。
06-08
import cv2 import numpy as np from matplotlib import pyplot as plt from sklearn.cluster import KMeans import math # 纸张尺寸标准 (单位: mm) PAPER_SIZES = { "A4": (210, 297), "B5": (176, 250), "A5": (148, 210) } # 使用Harris角点检测算法检测图像中的角点 def detect_corners_with_harris(image, block_size=2, ksize=3, k=0.04, threshold=0.01): if len(image.shape) == 3: gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) else: gray = image gray = np.float32(gray) dst = cv2.cornerHarris(gray, block_size, ksize, k) dst = cv2.dilate(dst, None) corner_img = image.copy() corners = [] threshold_val = threshold * dst.max() for i in range(dst.shape[0]): for j in range(dst.shape[1]): if dst[i, j] > threshold_val: corners.append((j, i)) cv2.circle(corner_img, (j, i), 5, (0, 0, 255), 2) return corner_img, corners # 从Harris角点中找出最可能的纸张四个角点 def find_paper_corners_using_harris(corners, image_shape, top_k=4): height, width = image_shape[:2] top_left = (0, 0) top_right = (width, 0) bottom_right = (width, height) bottom_left = (0, height) candidates = { 'top_left': [], 'top_right': [], 'bottom_right': [], 'bottom_left': [] } for corner in corners: x, y = corner distances = { 'top_left': np.sqrt((x - top_left[0]) ** 2 + (y - top_left[1]) ** 2), 'top_right': np.sqrt((x - top_right[0]) ** 2 + (y - top_right[1]) ** 2), 'bottom_right': np.sqrt((x - bottom_right[0]) ** 2 + (y - bottom_right[1]) ** 2), 'bottom_left': np.sqrt((x - bottom_left[0]) ** 2 + (y - bottom_left[1]) ** 2) } closest_category = min(distances, key=distances.get) candidates[closest_category].append((corner, distances[closest_category])) selected_corners = [] for category in candidates: if candidates[category]: candidates[category].sort(key=lambda x: x[1]) selected_corners.append(candidates[category][0][0]) if len(selected_corners) < 4: return None return order_points(np.array(selected_corners)) # 对点进行排序:左上,右上,右下,左下 def order_points(pts): rect = np.zeros((4, 2), dtype="float32") s = pts.sum(axis=1) rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] diff = np.diff(pts, axis=1) rect[1] = pts[np.argmin(diff)] rect[3] = pts[np.argmax(diff)] return rect # 检测图像中的纸张轮廓 def detect_paper_contour(image): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (5, 5), 0) edged = cv2.Canny(blurred, 50, 200) contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5] for contour in contours: peri = cv2.arcLength(contour, True) approx = cv2.approxPolyDP(contour, 0.02 * peri, True) if len(approx) == 4: return order_points(approx.reshape(4, 2)), gray return None, gray # 透视变换校正图像 def perspective_transform(image, points): def max_width_height(pts): width1 = np.sqrt(((pts[0][0] - pts[1][0]) ** 2) + ((pts[0][1] - pts[1][1]) ** 2)) width2 = np.sqrt(((pts[2][0] - pts[3][0]) ** 2) + ((pts[2][1] - pts[3][1]) ** 2)) max_width = max(int(width1), int(width2)) height1 = np.sqrt(((pts[0][0] - pts[3][0]) ** 2) + ((pts[0][1] - pts[3][1]) ** 2)) height2 = np.sqrt(((pts[1][0] - pts[2][0]) ** 2) + ((pts[1][1] - pts[2][1]) ** 2)) max_height = max(int(height1), int(height2)) return max_width, max_height points = order_points(points) max_width, max_height = max_width_height(points) dst = np.array([ [0, 0], [max_width - 1, 0], [max_width - 1, max_height - 1], [0, max_height - 1] ], dtype="float32") M = cv2.getPerspectiveTransform(points.astype("float32"), dst) warped = cv2.warpPerspective(image, M, (max_width, max_height)) return warped, M, max_width, max_height, points # 创建纸张区域掩码 def create_paper_mask(width, height): mask = np.ones((height, width), dtype=np.uint8) * 255 print(f"创建纸张掩码,尺寸: {mask.shape}, 类型: {mask.dtype}") return mask # 预处理图像以进行形状检测 def preprocess_image_for_shape_detection(image, paper_mask=None): # 转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 高斯模糊去噪 blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 可选:如果有纸张掩码,仅处理纸张内部区域 if paper_mask is not None: # 确保掩码尺寸与图像匹配 if paper_mask.shape[:2] != gray.shape[:2]: print(f"调整掩码尺寸: {paper_mask.shape} -> {gray.shape[:2]}") paper_mask = cv2.resize(paper_mask, (gray.shape[1], gray.shape[0]), interpolation=cv2.INTER_NEAREST) # 确保掩码是uint8类型 if paper_mask.dtype != np.uint8: print("转换掩码数据类型为uint8") paper_mask = paper_mask.astype(np.uint8) # 应用掩码 try: blurred = cv2.bitwise_and(blurred, blurred, mask=paper_mask) except cv2.error as e: print(f"应用掩码时出错: {e}") print(f"灰度图形状: {gray.shape}, 掩码形状: {paper_mask.shape}") return gray, blurred # 使用边缘检测方法检测图像中的形状区域 def detect_shapes_with_edge_detection(image, paper_mask=None, min_area_ratio=0.001, edge_canny_threshold1=30, edge_canny_threshold2=100): h, w = image.shape[:2] min_area = min_area_ratio * w * h print(f"最小面积阈值: {min_area} 像素") # 1. 调整图像大小(提升效率) if h > 500 or w > 500: scale = 500 / max(h, w) resized = cv2.resize(image, None, fx=scale, fy=scale) h_resized, w_resized = resized.shape[:2] print(f"调整图像大小: {h}x{w} -> {h_resized}x{w_resized}, 缩放比例: {scale}") else: resized = image.copy() scale = 1.0 h_resized, w_resized = h, w print(f"图像大小未调整: {h}x{w}") # 2. 多通道处理 - 结合亮度和饱和度 hsv = cv2.cvtColor(resized, cv2.COLOR_BGR2HSV) s_channel = hsv[:, :, 1] # 饱和度通道 v_channel = hsv[:, :, 2] # 亮度通道 # 3. 自适应阈值处理 _, s_binary = cv2.threshold(s_channel, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) v_binary = cv2.adaptiveThreshold(v_channel, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) # 4. 合并两个二值图像 binary = cv2.bitwise_or(s_binary, v_binary) # 5. 形态学操作优化 kernel = np.ones((3, 3), np.uint8) binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=1) # 去噪 binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=2) # 填充 # 6. 应用纸张掩码 if paper_mask is not None: paper_mask_resized = cv2.resize(paper_mask, (w_resized, h_resized), interpolation=cv2.INTER_NEAREST).astype( np.uint8) binary = cv2.bitwise_and(binary, paper_mask_resized) # 7. 边缘检测 edges = cv2.Canny(binary, edge_canny_threshold1, edge_canny_threshold2) # 8. 进一步形态学操作优化边缘 edges = cv2.dilate(edges, kernel, iterations=1) edges = cv2.erode(edges, kernel, iterations=1) # 9. 提取轮廓并过滤小区域 contours, _ = cv2.findContours(edges if edges.any() else binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) valid_contours = [] for contour in contours: area = cv2.contourArea(contour) if area > min_area: # 修复轮廓坐标缩放问题 if scale != 1.0: contour[:, :, 0] = np.round(contour[:, :, 0] * (w / w_resized)).astype(int) contour[:, :, 1] = np.round(contour[:, :, 1] * (h / h_resized)).astype(int) valid_contours.append(contour) print(f" 保留轮廓,面积: {area:.1f} 像素") print(f"总共检测到 {len(valid_contours)} 个有效形状区域") return valid_contours, binary, edges # 优化角点检测,特别是针对菱形的角点 def refine_corners(contour, epsilon_factor=0.02): """优化轮廓的角点检测,特别是针对菱形等形状""" # 原始多边形近似 peri = cv2.arcLength(contour, True) approx = cv2.approxPolyDP(contour, epsilon_factor * peri, True) # 如果已经是4个点,尝试进一步优化 if len(approx) == 4: # 计算轮廓的矩 M = cv2.moments(contour) if M["m00"] != 0: cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) # 将点按距离中心点的角度排序 points = approx.reshape(4, 2) angles = [] for point in points: dx = point[0] - cx dy = point[1] - cy angle = np.arctan2(dy, dx) angles.append(angle) # 按角度排序点 sorted_indices = np.argsort(angles) sorted_points = points[sorted_indices] # 重新排列为标准顺序(左上、右上、右下、左下) ordered_points = order_points(sorted_points) return ordered_points.reshape(-1, 1, 2).astype(np.int32) return approx # 识别物体形状,优化近似参数和判断逻辑 def identify_shape(contour): # 优化角点检测 approx = refine_corners(contour) num_sides = len(approx) area = cv2.contourArea(contour) # 计算轮廓周长,避免除以零 peri = cv2.arcLength(contour, True) if peri == 0: return "不规则形状" circularity = 4 * np.pi * area / (peri ** 2) if peri != 0 else 0 # 椭圆拟合(增加轮廓点数判断,避免报错) if len(contour) >= 5: try: ellipse = cv2.fitEllipse(contour) (center, axes, orientation) = ellipse major_axis = max(axes) minor_axis = min(axes) eccentricity = np.sqrt(1 - (minor_axis / major_axis) ** 2) if major_axis != 0 else 0 ellipse_area = np.pi * (major_axis / 2) * (minor_axis / 2) ellipse_ratio = area / ellipse_area if ellipse_area > 0 else 0 # 椭圆判定条件 if 0.8 <= ellipse_ratio <= 1.2 and 0.5 < eccentricity < 0.95: return "椭圆" except: pass # 多边形形状判断 if num_sides == 3: return "三角形" elif num_sides == 4: # 计算最小外接矩形 rect = cv2.minAreaRect(contour) (x, y), (width, height), angle = rect if min(width, height) == 0: # 避免除以0 aspect_ratio = 0 else: aspect_ratio = max(width, height) / min(width, height) # 计算轮廓的边界框 x, y, w, h = cv2.boundingRect(contour) bounding_ratio = max(w, h) / min(w, h) if min(w, h) > 0 else 0 # 计算四个顶点的坐标 points = approx.reshape(4, 2) # 计算相邻边之间的角度 angles = [] for i in range(4): p1 = points[i] p2 = points[(i + 1) % 4] p3 = points[(i + 2) % 4] v1 = p2 - p1 v2 = p3 - p2 dot_product = np.dot(v1, v2) cross_product = np.cross(v1, v2) angle = np.arctan2(cross_product, dot_product) * 180 / np.pi angles.append(abs(angle)) # 检查是否有直角 has_right_angle = any(abs(angle - 90) < 15 for angle in angles) # 计算边长 side_lengths = [] for i in range(4): p1 = points[i] p2 = points[(i + 1) % 4] side_length = np.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2) side_lengths.append(side_length) # 检查四边是否大致相等(菱形判定) avg_length = sum(side_lengths) / 4 is_rhombus = all(abs(l - avg_length) < avg_length * 0.2 for l in side_lengths) # 菱形判定条件:没有直角,四边大致相等,并且不是正方形 if not has_right_angle and is_rhombus and aspect_ratio < 1.5 and bounding_ratio > 1.2: return "菱形" # 正方形判定 if 0.85 <= aspect_ratio <= 1.15: return "正方形" # 矩形判定 else: return "矩形" elif circularity > 0.85: # 圆形判定 return "圆形" elif 5 <= num_sides <= 10: return f"{num_sides}边形" else: return "不规则形状" # 计算物体的几何属性 def calculate_properties(contour, pixel_to_mm=1.0): rect = cv2.minAreaRect(contour) width_px, height_px = rect[1] dimensions_px = sorted([width_px, height_px], reverse=True) length_px, width_px = dimensions_px length_mm = length_px * pixel_to_mm width_mm = width_px * pixel_to_mm area_px = cv2.contourArea(contour) area_mm = area_px * (pixel_to_mm ** 2) M = cv2.moments(contour) if M["m00"] != 0: cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) else: cx, cy = 0, 0 return length_mm, width_mm, area_mm, (cx, cy), length_px, width_px, area_px # 提取物体主要颜色(HSV) def extract_dominant_color(image, contour): mask = np.zeros(image.shape[:2], np.uint8) cv2.drawContours(mask, [contour], -1, 255, -1) hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 提取掩码区域的颜色 masked_pixels = hsv[mask == 255] if len(masked_pixels) == 0: return (0, 0, 0) # 计算主导颜色 (使用中位数更鲁棒) dominant_hsv = np.median(masked_pixels, axis=0) return tuple(map(int, dominant_hsv)) # 可视化检测过程的各个步骤 def visualize_detection_steps(image, corrected_image, paper_mask, shape_regions, binary_image, edge_image, result_img): """可视化检测过程的各个步骤,每个步骤显示在单独的窗口中""" # 显示原始图像 plt.figure(figsize=(10, 8)) plt.title('Original Image') plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) plt.axis('off') plt.tight_layout() plt.show() # 显示校正后的图像 plt.figure(figsize=(10, 8)) plt.title('Corrected Image') plt.imshow(cv2.cvtColor(corrected_image, cv2.COLOR_BGR2RGB)) plt.axis('off') plt.tight_layout() plt.show() # 显示边缘检测结果 plt.figure(figsize=(10, 8)) plt.title('Edge detection Results') plt.imshow(edge_image, cmap='gray') plt.axis('off') plt.tight_layout() plt.show() # 显示二值化结果 plt.figure(figsize=(10, 8)) plt.title('Binary Image') plt.imshow(binary_image, cmap='gray') plt.axis('off') plt.tight_layout() plt.show() # 显示形状检测结果 shape_image = corrected_image.copy() colors = [(0, 0, 255), (0, 255, 0), (255, 0, 0), (255, 255, 0), (0, 255, 255), (255, 0, 255), (128, 0, 0), (0, 128, 0)] for i, contour in enumerate(shape_regions): # 绘制优化后的角点 approx = refine_corners(contour) color = colors[i % len(colors)] cv2.drawContours(shape_image, [contour.astype(int)], -1, color, 2) # 标记角点 for point in approx: x, y = point[0] cv2.circle(shape_image, (x, y), 5, (0, 255, 0), -1) plt.figure(figsize=(10, 8)) plt.title('Shape detection Results (with corners)') plt.imshow(cv2.cvtColor(shape_image, cv2.COLOR_BGR2RGB)) plt.axis('off') plt.tight_layout() plt.show() # 显示最终识别结果 plt.figure(figsize=(10, 8)) plt.title('Shape recognition Results') plt.imshow(cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)) plt.axis('off') plt.tight_layout() plt.show() # 主函数 def main(image_path): image = cv2.imread(image_path) if image is None: print(f"错误:无法读取图像 {image_path}") return # 1. 使用Harris角点检测纸张边框 corner_img, corners = detect_corners_with_harris(image) paper_points = find_paper_corners_using_harris(corners, image.shape) # 初始化变量 corrected_image = image.copy() paper_mask = None pixel_to_mm = 1.0 paper_detected = False # 2. 透视变换校正图像 if paper_points is not None: # 执行透视变换 corrected_image, M, max_width, max_height, original_points = perspective_transform(image, paper_points) paper_detected = True print("成功检测并校正纸张轮廓") # 创建纸张掩码(仅处理纸张内部区域) paper_mask = create_paper_mask(max_width, max_height) # 计算物理尺寸转换因子 paper_width_mm, paper_height_mm = PAPER_SIZES["A4"] pixel_to_mm_x = paper_width_mm / max_width pixel_to_mm_y = paper_height_mm / max_height pixel_to_mm = (pixel_to_mm_x + pixel_to_mm_y) / 2.0 else: print("未检测到纸张轮廓 - 使用原始图像进行分析") h, w = image.shape[:2] paper_mask = create_paper_mask(w, h) # 3. 基于边缘检测和形态学操作检测几何形状 shape_regions, binary_image, edge_image = detect_shapes_with_edge_detection( corrected_image, paper_mask=paper_mask, min_area_ratio=0.001, # 降低最小面积阈值 edge_canny_threshold1=30, # Canny边缘检测阈值 edge_canny_threshold2=100 # Canny边缘检测阈值 ) # 如果没有检测到形状,尝试使用不同的参数 if not shape_regions: print("第一次尝试未检测到几何形状 - 尝试使用不同的参数") shape_regions, binary_image, edge_image = detect_shapes_with_edge_detection( corrected_image, paper_mask=paper_mask, min_area_ratio=0.0005, # 进一步降低最小面积阈值 edge_canny_threshold1=20, edge_canny_threshold2=80 ) if not shape_regions: print("未检测到几何形状 - 尝试调整参数") # 备用方案:直接在二值图像上查找轮廓 gray = cv2.cvtColor(corrected_image, cv2.COLOR_BGR2GRAY) _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) if paper_mask is not None: binary = cv2.bitwise_and(binary, binary, mask=paper_mask) contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) min_area = 0.0005 * binary.shape[0] * binary.shape[1] shape_regions = [c for c in contours if cv2.contourArea(c) > min_area] if not shape_regions: print("所有尝试均未检测到几何形状") return else: print(f"备用方案检测到 {len(shape_regions)} 个形状") edge_image = cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR) # 初始化结果列表 results = [] result_img = corrected_image.copy() # 处理每个形状区域 for i, contour in enumerate(shape_regions): # 提取主导颜色 hsv_color = extract_dominant_color(corrected_image, contour) rgb_color = cv2.cvtColor(np.uint8([[hsv_color]]), cv2.COLOR_HSV2BGR)[0][0] color_name = f"RGB({rgb_color[2]}, {rgb_color[1]}, {rgb_color[0]})" # 识别形状 shape = identify_shape(contour) # 计算几何属性 length_mm, width_mm, area_mm, center_px, length_px, width_px, area_px = calculate_properties( contour, pixel_to_mm ) # 存储结果 results.append({ "id": i + 1, "shape": shape, "length_mm": length_mm, "width_mm": width_mm, "area_mm": area_mm, "color": color_name, "hsv_color": hsv_color, "center_px": center_px, "length_px": length_px, "width_px": width_px, "area_px": area_px }) # 在图像上标注物体 color = (int(rgb_color[2]), int(rgb_color[1]), int(rgb_color[0])) cv2.drawContours(result_img, [contour], -1, color, 3) cv2.putText(result_img, f"{i + 1}({shape})", (int(center_px[0]), int(center_px[1])), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2) # 可视化检测步骤 visualize_detection_steps(image, corrected_image, paper_mask, shape_regions, binary_image, edge_image, result_img) # 打印分析结果 print("\n===== 图像分析结果 =====") print(f"检测到 {len(results)} 个几何图形") print("-" * 70) for obj in results: print(f"图形 {obj['id']}:") print(f" 形状: {obj['shape']}") print(f" 长度: {obj['length_mm']:.2f} mm ({obj['length_px']:.1f} 像素)") print(f" 宽度: {obj['width_mm']:.2f} mm ({obj['width_px']:.1f} 像素)") print(f" 面积: {obj['area_mm']:.2f} mm² ({obj['area_px']:.1f} 像素²)") print(f" 颜色: {obj['color']} (HSV: {obj['hsv_color']})") print(f" 中心坐标: ({obj['center_px'][0]}, {obj['center_px'][1]})") print("-" * 70) if __name__ == "__main__": image_path = "ALL2.jpg" # 替换为实际图像路径 main(image_path) 上述代码对图像进行二值化操作部分有点问题,导致一些几何形状的角钝化,可能因此导致形状判定不正确的问题,请修改一下这串代码,给出完整的修改后的代码
06-08
import cv2 import numpy as np import platform from collections collections import defaultdict import time # 纸张物理参数(毫米) PAPER_WIDTH_MM = 170.0 PAPER_HEIGHT_MM = 257.0 BORDER_WIDTH_MM = 20.0 # 边框宽度 class PaperGeometryDetector: def __init__(self): # 3D参考点(纸张四角,以中心为原点) self.object_3d_points = np.array( [ [-PAPER_WIDTH_MM / 2, -PAPER_HEIGHT_MM / 2, 0], [PAPER_WIDTH_MM / 2, -PAPER_HEIGHT_MM / 2, 0], [PAPER_WIDTH_MM / 2, PAPER_HEIGHT_MM / 2, 0], [-PAPER_WIDTH_MM / 2, PAPER_HEIGHT_MM / 2, 0], ], dtype=np.float32, ) # 摄像头内参(可根据实际校准修改) self.camera_matrix = np.array( [[640, 0, 320], [0, 640, 240], [0, 0, 1]], dtype=np.float32 ) self.dist_coeffs = np.zeros((5, 1), dtype=np.float32) # 畸变系数 self.try_open_camera() self.frame_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) if hasattr(self, 'cap') else 640 self.frame_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) if hasattr(self, 'cap') else 480 # 调整内参以匹配实际分辨率 self.camera_matrix[0, 2] = self.frame_width / 2 self.camera_matrix[1, 2] = self.frame_height / 2 self.pixel_per_mm = 1.0 # 像素-毫米转换比例 self.distance = 0.0 # 摄像头到纸张的距离(毫米) self.edges = None self.finalWarped = None self.frame = None self.corners = None self.warped = None def try_open_camera(self, camera_id=0): """打开摄像头并设置分辨率""" if platform.system().lower() == "windows": cap = cv2.VideoCapture(camera_id, cv2.CAP_DSHOW) else: cap = cv2.VideoCapture(camera_id) # 设置分辨率(640x480兼容大多数摄像头) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) if cap.isOpened(): print(f"成功打开摄像头: {camera_id},分辨率: 640x480") self.cap = cap return True else: cap.release() print("未找到可用的摄像头") return False def detect_corners(self, frame): """检测纸张四角并排序""" def angle_sort(pt): vec = pt - center return np.arctan2(vec[1], vec[0]) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (5, 5), 0) ret, binary_img = cv2.threshold(blurred, 127, 255, cv2.THRESH_BINARY_INV) # 反相阈值,突出白色纸张 edges = cv2.Canny(binary_img, 50, 150) contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) valid_contours = [] for cnt in contours: area = cv2.contourArea(cnt) if area < 5000: # 过滤小轮廓 continue epsilon = 0.02 * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) if len(approx) == 4 and cv2.isContourConvex(approx): # 四边形形且凸多边形 corners = approx.reshape(4, 2) valid_contours.append((area, corners)) if not valid_contours: return None # 选择面积最大的轮廓(纸张) valid_contours.sort(key=lambda x: x[0], reverse=True) largest_area, corners = valid_contours[0] # 按角度排序角点(确保顺序一致) center = corners.mean(axis=0) sorted_corners = sorted(corners, key=angle_sort) start_idx = np.argmin([pt[1] for pt in sorted_corners]) # 顶部角点为起点 ordered = np.roll(sorted_corners, -start_idx, axis=0) return np.array(ordered, dtype=np.float32) def order_points(self, pts): """按左上、右上、右下、左下排序点""" rect = np.zeros((4, 2), dtype=np.float32) s = pts.sum(axis=1) diff = np.diff(pts, axis=1) rect[0] = pts[np.argmin(s)] # 左上 rect[2] = pts[np.argmax(s)] # 右下 rect[1] = pts[np.argmin(diff)] # 右上 rect[3] = pts[np.argmax(diff)] # 左下 return rect def calculate_distance_pnp(self, image_points): """通过PNP算法计算摄像头到纸张的距离(毫米)""" if image_points is None or len(image_points) != 4: return None ordered_points = self.order_points(image_points) try: success, rvec, tvec = cv2.solvePnP( self.object_3d_points, ordered_points, self.camera_matrix, self.dist_coeffs ) if success: # 距离为平移向量的模长 distance = np.linalg.norm(tvec) return distance except Exception as e: print(f"PNP计算失败: {e}") return None def warp_perspective_from_corners(self, frame, corners, output_size=(int(PAPER_WIDTH_MM * 2), int(PAPER_HEIGHT_MM * 2))): """透视变换矫正纸张区域""" if corners is None or len(corners) != 4: return None, None, None rect = self.order_points(corners) dst = np.array([ [0, 0], [output_size[0] - 1, 0], [output_size[0] - 1, output_size[1] - 1], [0, output_size[1] - 1] ], dtype=np.float32) M = cv2.getPerspectiveTransform(rect, dst) Minv = cv2.getPerspectiveTransform(dst, rect) warped = cv2.warpPerspective(frame, M, output_size) return warped, M, Minv def resize_warped(self): """缩放矫正后的后的图像,计算像素-毫米比例""" up_points = (int(PAPER_WIDTH_MM * 3), int(PAPER_HEIGHT_MM * 3)) # 按纸张尺寸放大3倍 resized_up = cv2.resize(self.warped, up_points, interpolation=cv2.INTER_LINEAR) self.pixel_per_mm = up_points[0] / PAPER_WIDTH_MM # 像素/毫米比例(水平方向) return resized_up def pixels_to_mm(self, pixels): """像素转毫米""" return pixels / self.pixel_per_mm def mm_to_pixels(self, mm): """毫米转像素""" return mm * self.pixel_per_mm def run(self): """执行纸张检测和预处理""" if not hasattr(self, 'cap'): return False ret, frame = self.cap.read() if not ret: return False self.frame = cv2.flip(frame, 1) # 镜像翻转,便于操作 self.corners = self.detect_corners(self.frame) if self.corners is None: return False # 计算摄像头到纸张的距离(毫米) self.distance = self.calculate_distance_pnp(self.corners) if self.distance is None: return False # 透视变换并缩放 self.warped, M, Minv = self.warp_perspective_from_corners(self.frame, self.corners) if self.warped is None: return False self.finalWarped = self.resize_warped() # 边缘检测(用于后续形状识别) gray = cv2.cvtColor(self.finalWarped, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (3, 3), 0) ret, binary_img = cv2.threshold(blurred, 127, 255, cv2.THRESH_BINARY_INV) self.edges = cv2.Canny(binary_img, 50, 150) # 遮盖边框区域(避免干扰) edge_border = int(self.mm_to_pixels(BORDER_WIDTH_MM)) + 5 self.edges[:edge_border, :] = 0 # 顶部 self.edges[-edge_border:, :] = 0 # 底部 self.edges[:, :edge_border] = 0 # 左侧 self.edges[:, -edge_border:] = 0 # 右侧 # 绘制角点和距离信息 for i, corner in enumerate(self.corners): cv2.circle(self.frame, tuple(corner.astype(int)), 5, (0, 255, 0), -1) cv2.putText(self.frame, f"Corner {i}", tuple(corner.astype(int) + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 1) # 显示距离(转换为厘米) cv2.putText(self.frame, f"纸张距离: {self.distance / 10:.1f}cm", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) return True class ShapeDetector: """形状检测与尺寸测量(含矩形扩展与内部边框识别)""" def __init__(self, geo): self.geo = geo self.detection_history = defaultdict(list) # 存储检测历史 self.expand_pixels = 8 # 矩形向外扩展的像素数(可根据需要调整) self.inner_borders = [] # 存储检测到的内部边框 def detect_inner_borders(self, roi): """检测ROI区域内的内部边框""" # 对ROI区域进行边缘检测 gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) blurred_roi = cv2.GaussianBlur(gray_roi, (3, 3), 0) edges_roi = cv2.Canny(blurred_roi, 50, 150) # 查找内部轮廓 inner_contours, _ = cv2.findContours(edges_roi, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) inner_borders = [] for cnt in inner_contours: area = cv2.contourArea(cnt) if 50 < area < 10000: # 过滤过小或过大的轮廓 peri = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.02 * peri, True) if len(approx) == 4: # 只保留四边形边框 inner_borders.append(approx) return inner_borders def expand_rectangle(self, x, y, w, h, img_shape): """将矩形向外扩展指定像素,确保不超出图像边界""" x_expanded = max(0, x - self.expand_pixels) y_expanded = max(0, y - self.expand_pixels) w_expanded = min(img_shape[1] - x_expanded, w + 2 * self.expand_pixels) h_expanded = min(img_shape[0] - y_expanded, h + 2 * self.expand_pixels) return x_expanded, y_expanded, w_expanded, h_expanded def detect_shapes(self): """检测纸张上的形状并测量尺寸(增强矩形处理)""" if self.geo.edges is None or self.geo.finalWarped is None: return [] contours, _ = cv2.findContours(self.geo.edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) detected_shapes = [] img_shape = self.geo.finalWarped.shape self.inner_borders = [] # 重置内部边框列表 for cnt in contours: area_px = cv2.contourArea(cnt) if area_px < 100: # 过滤小轮廓 continue # 形状识别 peri = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.03 * peri, True) vertices = len(approx) shape = "unknown" size_mm = 0.0 # 三角形(3条边) if vertices == 3: shape = "triangle" # 计算边长(取平均值) sides = [] for i in range(3): p1 = approx[i][0] p2 = approx[(i + 1) % 3][0] side_px = np.linalg.norm(p1 - p2) sides.append(self.geo.pixels_to_mm(side_px)) size_mm = np.mean(sides) # 平均边长 # 四边形(4条边) elif vertices == 4: x, y, w, h = cv2.boundingRect(approx) aspect_ratio = w / h # 扩展矩形边界并检测内部边框 x_exp, y_exp, w_exp, h_exp = self.expand_rectangle(x, y, w, h, img_shape) roi = self.geo.finalWarped[y_exp:y_exp + h_exp, x_exp:x_exp + w_exp] inner_borders = self.detect_inner_borders(roi) # 存储内部边框(调整坐标到原始图像) for border in inner_borders: border_shifted = border + np.array([[x_exp, y_exp]], dtype=np.int32) self.inner_borders.append(border_shifted) # 区分正方形和矩形 if 0.95 <= aspect_ratio <= 1.05: shape = "square" else: shape = "rectangle" # 计算边长(取平均值) sides = [] for i in range(4): p1 = approx[i][0] p2 = approx[(i + 1) % 4][0] side_px = np.linalg.norm(p1 - p2) sides.append(self.geo.pixels_to_mm(side_px)) size_mm = np.mean(sides) # 平均边长 # 圆形(通过圆度判断) else: circularity = 4 * np.pi * area_px / (peri ** 2) if peri > 0 else 0 if 0.7 < circularity < 1.3: shape = "circle" # 计算直径(最小外接圆) (x, y), radius_px = cv2.minEnclosingCircle(cnt) size_mm = self.geo.pixels_to_mm(radius_px * 2) # 直径 # 只保留有效形状 if shape in ["triangle", "square", "rectangle", "circle"]: detected_shapes.append((shape, size_mm, cnt)) # 记录历史数据(用于后续平均计算) self.detection_history[shape].append(size_mm) return detected_shapes def draw_detections(self, shapes): """在图像上绘制检测结果""" if self.geo.finalWarped is None: return # 绘制形状 for shape, size_mm, cnt in shapes: # 绘制轮廓 cv2.drawContours(self.geo.finalWarped, [cnt], -1, (0, 255, 0), 2) # 计算中心坐标 M = cv2.moments(cnt) if M["m00"] == 0: continue cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) # 显示形状和尺寸 label = f"{shape}: {size_mm:.1f}mm" if shape == "circle": label = f"{shape} (直径): {size_mm:.1f}mm" cv2.putText(self.geo.finalWarped, label, (cX - 50, cY), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2) # 绘制扩展区域和内部边框 for border in self.inner_borders: cv2.drawContours(self.geo.finalWarped, [border], -1, (255, 0, 0), 2) # 蓝色框标记内部边框 def print_stats(self, duration=3): """打印指定时间内的检测统计结果""" print(f"\n===== {duration}秒检测统计 =====") print(f"纸张尺寸: {PAPER_WIDTH_MM}mm x {PAPER_HEIGHT_MM}mm") print(f"摄像头到纸张距离: {self.geo.distance / 10:.1f}cm") if not self.detection_history: print("未检测到有效形状") return for shape, sizes in self.detection_history.items(): avg_size = np.mean(sizes) print(f"\n{shape}:") print(f" 平均尺寸: {avg_size:.1f}mm") print(f" 检测次数: {len(sizes)}") print("================================\n") def main(): geo = PaperGeometryDetector() if not hasattr(geo, 'cap'): # 检查摄像头是否成功打开 return shape_detector = ShapeDetector(geo) detection_duration = 3 # 统计时长(秒) start_time = time.time() print(f"程序启动,将在{detection_duration}秒后输出统计结果(按'q'退出)") while True: if cv2.waitKey(1) & 0xFF == ord('q'): break # 执行纸张检测 success = geo.run() if hasattr(geo, 'run') else False if not success: # 显示未检测到纸张的提示 if hasattr(geo, 'frame') and geo.frame is not None: cv2.putText(geo.frame, "未检测到纸张", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.imshow("原始图像", geo.frame) continue # 检测形状并绘制结果 shapes = shape_detector.detect_shapes() shape_detector.draw_detections(shapes) # 显示图像 cv2.imshow("原始图像", geo.frame) cv2.imshow("矫正后纸张", geo.finalWarped) cv2.imshow("边缘检测", geo.edges) # 超时后输出统计结果并重置 if time.time() - start_time >= detection_duration: shape_detector.print_stats(detection_duration) shape_detector.detection_history.clear() # 清空历史数据 start_time = time.time() # 释放资源 if hasattr(geo, 'cap'): geo.cap.release() cv2.destroyAllWindows() print("程序已退出") if __name__ == "__main__": main()
08-03
import cv2 import numpy as np import time def detect_a4_paper_and_shapes(frame): """ 检测A4纸并识别其上的形状 参数: frame: 输入的视频帧 返回: 处理后的视频帧,包含检测结果 """ # 记录开始时间,用于计算处理帧率 start_time = time.time() # 调整图像大小,加快处理速度 frame = cv2.resize(frame, (800, 600)) output_frame = frame.copy() # 转换为灰度图并进行模糊处理,减少噪点 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (7, 7), 0) # 边缘检测 edged = cv2.Canny(blurred, 30, 120) # 查找轮廓 contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) a4_contour = None # 筛选出A4纸轮廓(四边形) for contour in contours: # 忽略过小的轮廓 if cv2.contourArea(contour) < 5000: continue # 轮廓近似 peri = cv2.arcLength(contour, True) approx = cv2.approxPolyDP(contour, 0.02 * peri, True) # A4纸是四边形,所以寻找有4个顶点的轮廓 if len(approx) == 4: # 检查是否为凸四边形 if cv2.isContourConvex(approx): a4_contour = approx break # 如果找到了A4纸 if a4_contour is not None: # 在原图上绘制A4纸轮廓 cv2.drawContours(output_frame, [a4_contour], -1, (0, 0, 255), 2) cv2.putText(output_frame, "A4 Paper", (a4_contour[0][0][0], a4_contour[0][0][1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) # 准备透视变换 pts = a4_contour.reshape(4, 2) rect = np.zeros((4, 2), dtype="float32") # 按顺序找到四个点:左上、右上、右下、左下 s = pts.sum(axis=1) rect[0] = pts[np.argmin(s)] # 左上点(x+y最小) rect[2] = pts[np.argmax(s)] # 右下点(x+y最大) diff = np.diff(pts, axis=1) rect[1] = pts[np.argmin(diff)] # 右上点(x-y最小) rect[3] = pts[np.argmax(diff)] # 左下点(x-y最大) # 计算A4纸的宽度和高度 (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]], dtype="float32") # 执行透视变换,矫正A4纸 try: M = cv2.getPerspectiveTransform(rect, dst) warped = cv2.warpPerspective(frame, M, (maxWidth, maxHeight)) # 在矫正后的图像中检测形状 gray_warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY) # 自适应阈值处理,提高不同光照下的鲁棒性 thresh = cv2.adaptiveThreshold(gray_warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) # 寻找形状轮廓 contours_warped, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 识别每个轮廓的形状 for cnt in contours_warped: # 忽略过小的轮廓 area = cv2.contourArea(cnt) if area < 100: continue # 轮廓近似 peri = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.03 * peri, True) # 确定形状 shape = "Unknown" if len(approx) == 3: shape = "三角形" elif len(approx) == 4: # 检查是否为正方形 x, y, w, h = cv2.boundingRect(approx) aspect_ratio = float(w) / h if 0.95 <= aspect_ratio <= 1.05: shape = "正方形" else: shape = "长方形" else: # 检测圆形 perimeter = cv2.arcLength(cnt, True) circularity = 4 * np.pi * area / (perimeter ** 2) if perimeter > 0 else 0 if 0.7 < circularity < 1.3: shape = "圆形" elif 5 <= len(approx) <= 8: shape = "多边形" # 计算形状中心并标记 M = cv2.moments(cnt) if M["m00"] != 0: cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) # 绘制轮廓和形状名称 cv2.drawContours(warped, [cnt], -1, (0, 255, 0), 2) cv2.putText(warped, shape, (cX - 30, cY), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2) # 将矫正后的图像放置在输出帧的左上角 h, w = warped.shape[:2] if h < output_frame.shape[0] and w < output_frame.shape[1]: output_frame[0:h, 0:w] = warped else: # 如果矫正后的图像太大,缩小后再放置 scale = min(output_frame.shape[0] / h, output_frame.shape[1] / w) warped = cv2.resize(warped, (int(w * scale), int(h * scale))) h, w = warped.shape[:2] output_frame[0:h, 0:w] = warped except Exception as e: print(f"透视变换出错: {e}") # 计算并显示帧率 fps = 1.0 / (time.time() - start_time) cv2.putText(output_frame, f"FPS: {int(fps)}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) return output_frame def main(): """主函数:实时处理摄像头视频流""" # 打开摄像头(0表示默认摄像头) cap = cv2.VideoCapture(0) # 检查摄像头是否成功打开 if not cap.isOpened(): print("无法打开摄像头,请检查设备是否正常连接") return print("程序已启动,正在检测A4纸上的形状...") print("按 'q' 键退出程序") try: while True: # 读取一帧画面 ret, frame = cap.read() if not ret: print("无法获取视频帧,程序将退出") break # 处理帧并检测形状 processed_frame = detect_a4_paper_and_shapes(frame) # 显示处理后的画面 cv2.imshow("A4纸形状检测", processed_frame) # 按 'q' 键退出 if cv2.waitKey(1) & 0xFF == ord('q'): break except KeyboardInterrupt: print("程序被用户中断") finally: # 释放资源 cap.release() cv2.destroyAllWindows() print("程序已退出") if __name__ == "__main__": main() print识别的有效图形和有效图像在整张图片中的占比和a4纸在整张实施图片的占比
07-31
资源下载链接为: https://pan.quark.cn/s/f989b9092fc5 HttpServletRequestWrapper 是 Java Servlet API 中的一个工具类,位于 javax.servlet.http 包中,用于对 HttpServletRequest 对象进行封装,从而在 Web 应用中实现对 HTTP 请求的拦截、修改或增强等功能。通过继承该类并覆盖相关方法,开发者可以轻松地自定义请求处理逻辑,例如修改请求参数、添加请求头、记录日志等。 参数过滤:在请求到达处理器之前,可以对请求参数进行检查或修改,例如去除 URL 编码、过滤敏感信息或进行安全检查。 请求头操作:可以修改或添加请求头,比如设置自定义的 Content-Type 或添加认证信息。 请求属性扩展:在原始请求的基础上添加自定义属性,供后续处理使用。 日志记录:在处理请求前记录请求信息,如 URL、参数、请求头等,便于调试和监控。 跨域支持:通过添加 CORS 相关的响应头,允许来自不同源的请求。 HttpServletRequestWrapper 通过继承 HttpServletRequest 接口并重写其方法来实现功能。开发者可以在重写的方法中添加自定义逻辑,例如在获取参数时进行过滤,或在读取请求体时进行解密。当调用这些方法时,实际上是调用了包装器中的方法,从而实现了对原始请求的修改或增强。 以下是一个简单的示例,展示如何创建一个用于过滤请求参数的包装器: 在 doFilter 方法中,可以使用 CustomRequestWrapper 包装原始请求: 这样,每当调用 getParameterValues 方法时,都会先经过自定义的过滤逻辑。 HttpServletRequestWrapper 是 Java Web 开发中一个强大的工具,它提供了灵活的扩展性,允许开发者
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值