基于Python提取相册页照片:白色背景下对比度优化方案
问题描述
我有一本实体相册,每页可能粘有一张或多张照片。我拍摄了每页的照片并放入同一文件夹,希望用Python批量提取其中的照片。现有OpenCV脚本会检测到过多轮廓(包含照片内部的轮廓),请问在页面背景为白色时,有什么合适的替代方法来调整对比度?
原脚本代码
# Read the image img = cv2.imread("images/" + image) # Convert the image to grayscale gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Show gray image cv2.imshow('Gray Image', gray) cv2.waitKey(0) blurred = cv2.GaussianBlur(gray, (5, 5), 0) # Apply edge detection using the Canny edge detector edged = cv2.Canny(blurred, 50, 150) contours, _ = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) min_area = 50000 filtered_contours = [cnt for cnt in contours if min_area < cv2.contourArea(cnt)] extracted_photos = [] for i, contour in enumerate(filtered_contours): x, y, w, h = cv2.boundingRect(contour) extracted_photos.append(img[y:y+h, x:x+w]) # Uncomment the following line to save individual photos # cv2.imwrite(f'photo_{i}.jpg', image[y:y+h, x:x+w]) # Show the extracted photos cv2.imshow('Original Image', img) cv2.waitKey(0) for i, photo in enumerate(extracted_photos): cv2.imshow(f'Photo {i}', photo) cv2.waitKey(0) cv2.destroyAllWindows()
效果展示
原始照片

灰度照片

轮廓检测结果

解决方案
针对白色背景的相册页面,可通过以下方法优化轮廓检测,避免提取到照片内部的轮廓:
1. 全局阈值分割(替代Canny边缘检测)
利用白色背景与照片区域的灰度差异,直接通过二值化分割出照片区域,这种方法不会检测到照片内部的细节边缘:
import cv2 import numpy as np # 读取图像 img = cv2.imread("images/" + image) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯模糊降噪 blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 反转二值化:将白色背景转为黑色,照片区域转为白色 _, thresh = cv2.threshold(blurred, 220, 255, cv2.THRESH_BINARY_INV) # 仅提取最外层轮廓 contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 过滤小面积轮廓(根据实际照片大小调整阈值) min_area = 50000 filtered_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_area] # 提取并保存照片 extracted_photos = [] for i, cnt in enumerate(filtered_contours): x, y, w, h = cv2.boundingRect(cnt) extracted_photos.append(img[y:y+h, x:x+w]) # cv2.imwrite(f'extracted_photo_{i}.jpg', img[y:y+h, x:x+w]) # 查看结果 cv2.imshow('Threshold Image', thresh) cv2.waitKey(0) for i, photo in enumerate(extracted_photos): cv2.imshow(f'Extracted Photo {i}', photo) cv2.waitKey(0) cv2.destroyAllWindows()
2. 自适应阈值分割(应对光照不均)
如果拍摄时光照不均匀,部分背景偏暗,用自适应阈值能根据局部区域灰度调整分割标准,提升准确性:
# 替换全局阈值部分的代码 thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
3. 对比度增强(缩小背景与照片的灰度差距)
若照片与背景灰度差异不明显,先增强对比度再分割:
# 使用CLAHE限制对比度自适应直方图均衡化,避免局部过曝 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) equalized = clahe.apply(gray) # 后续步骤同全局阈值分割 blurred = cv2.GaussianBlur(equalized, (5,5), 0) _, thresh = cv2.threshold(blurred, 220, 255, cv2.THRESH_BINARY_INV)
4. 形态学操作(去除噪点轮廓)
阈值分割后若存在小噪点轮廓,用形态学操作消除:
# 创建5x5的结构元素 kernel = np.ones((5,5), np.uint8) # 闭操作填充照片内部的小孔洞,开操作去除背景噪点 thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) # thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
内容的提问来源于stack exchange,提问作者Mart
相关产品推荐
相关产品推荐

