图像轮廓与黑色区域分析工具
概述
本工具用于分析图像中的大轮廓及其内部黑色区域,计算轮廓面积、白黑像素比值等关键参数,并将分析结果可视化标注在图像上。适用于需要对图像中特定区域进行定量分析的场景,如工业检测、材料分析等。
功能特点
- 自动识别图像中的大轮廓区域
- 分析轮廓内的黑色区域并计算面积
- 计算轮廓的白黑像素比值
- 可视化标注所有分析结果(轮廓、面积、比值等)
- 批量处理图像并生成汇总报告
函数说明
核心函数
calculate_contour_ratios(img_path)
功能:分析单张图像的轮廓和黑色区域,返回分析结果并保存标注后的图像
参数:
img_path:图像文件的路径(字符串)
返回值:
- 列表,每个元素为元组
(面积, 白像素/黑像素比值),对应每个符合条件的大轮廓 - 若图像读取失败或未检测到有效轮廓,返回空列表
流程:
- 调用预处理函数处理图像
- 提取并过滤大轮廓
- 对每个大轮廓计算面积和白黑比
- 分析轮廓内的黑色区域
- 绘制所有标注信息
- 保存结果图像并返回分析数据
辅助函数
_preprocess_image(img_path)
功能:图像预处理,包括读取图像、转灰度图、二值化和形态学操作
返回值:
- 原图、灰度图、处理后的二值图(三元组)
- 若图像读取失败,返回
(None, None, None)
_extract_large_contours(binary_img, min_area=105000)
功能:从二值图像中提取轮廓并过滤出面积足够大的轮廓
参数:
binary_img:二值化图像min_area:轮廓面积阈值,默认105000
返回值:符合面积条件的轮廓列表
_process_black_regions(contour, binary_img)
功能:处理单个大轮廓内的黑色区域,返回符合条件的黑色区域信息
参数:
contour:大轮廓坐标数组binary_img:二值化图像
返回值:列表,每个元素为字典 {"contour": 轮廓坐标, "area": 面积}
_draw_large_contour_info(img, contour, area, ratio)
功能:在图像上绘制大轮廓的面积和白黑比标注
参数:
img:待绘制的图像contour:大轮廓坐标area:轮廓面积ratio:白黑像素比值
_draw_black_region_info(img, black_regions)
功能:在图像上绘制黑色区域的轮廓和面积标注
参数:
img:待绘制的图像black_regions:黑色区域信息列表(_process_black_regions的返回值)
_save_result_image(img_path, img)
功能:保存处理后的图像并返回保存路径
参数:
img_path:原始图像路径img:带标注的图像
返回值:保存的图像路径
使用方法
单张图像分析
result = calculate_contour_ratios("path/to/your/image.jpg")
if result:
print("分析结果:")
for area, ratio in result:
print(f"面积: {area:.0f}, 白黑比: {ratio:.2f}")
else:
print("分析失败")
批量图像分析
image_paths = ["image1.jpg", "image2.jpg", "image3.jpg"]
all_results = []
for path in image_paths:
print(f"处理图像: {path}")
results = calculate_contour_ratios(path)
if results:
all_results.extend(results)
# 打印汇总结果
print(f"\n总分析轮廓数: {len(all_results)}")
输出说明
-
控制台输出:
- 处理进度信息
- 每个图像的轮廓数量
- 每个轮廓的面积和白黑比
- 汇总统计信息
-
图像输出:
- 在原始图像路径下生成标注后的图像(文件名格式:
原文件名_contour.扩展名) - 红色线:大轮廓
- 绿色线:黑色区域轮廓
- 蓝色文本:大轮廓面积
- 绿色文本:白黑像素比值
- 黄色文本:黑色区域面积
- 在原始图像路径下生成标注后的图像(文件名格式:
参数调整
可根据实际需求调整以下参数:
- 大轮廓面积阈值:
_extract_large_contours函数中的min_area参数 - 黑色区域面积阈值:
_process_black_regions函数中的min_black_area参数 - 形态学操作核大小:
_process_black_regions函数中的small_kernel定义 - 腐蚀膨胀迭代次数:
_process_black_regions函数中的iterations参数 - 二值化阈值:
_preprocess_image函数中的阈值参数(当前为20)
调整这些参数可以优化不同类型图像的分析效果。
import cv2
import numpy as np
import os
def _preprocess_image(img_path):
"""预处理图像:读取、转灰度、二值化和形态学操作"""
# 读取图像
img = cv2.imread(img_path)
if img is None:
return None, None, None
# 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 阈值分割(提取白色区域)
ret, binary = cv2.threshold(gray, 20, 255, cv2.THRESH_BINARY)
# 形态学操作:增强白色区域连通度
kernel = np.ones((7, 7), np.uint8)
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=1)
closing = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel, iterations=3)
return img, gray, closing
def _extract_large_contours(binary_img, min_area=105000):
"""提取并过滤出面积足够大的轮廓"""
contours, hierarchy = cv2.findContours(
binary_img,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
return [cnt for cnt in contours if cv2.contourArea(cnt) >= min_area]
def _process_black_regions(contour, binary_img):
"""处理单个大轮廓内的黑色区域,返回符合条件的黑色区域信息"""
# 创建轮廓掩码
mask = np.zeros_like(binary_img)
cv2.drawContours(mask, [contour], -1, 255, -1)
# 提取轮廓内的区域并反转以突出黑色区域
contour_region = cv2.bitwise_and(binary_img, mask)
black_regions = cv2.bitwise_not(contour_region)
# 阈值处理确保纯黑白
_, black_binary = cv2.threshold(black_regions, 127, 255, cv2.THRESH_BINARY)
# 形态学操作:断开细小连接
small_kernel = np.ones((7, 7), np.uint8)
eroded = cv2.erode(black_binary, small_kernel, iterations=3)
dilated = cv2.dilate(eroded, small_kernel, iterations=1)
dilated = cv2.bitwise_and(dilated, mask) # 确保在大轮廓内
# 查找黑色区域轮廓
black_contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 过滤并计算面积
min_black_area = 8000
return [
{"contour": cnt, "area": cv2.contourArea(cnt)}
for cnt in black_contours
if cv2.contourArea(cnt) >= min_black_area
]
def _draw_large_contour_info(img, contour, area, ratio):
"""在图像上绘制大轮廓的面积和比值信息"""
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.7
ratio_color = (0, 255, 0)
area_color = (255, 0, 0)
thickness = 2
x, y, w, h = cv2.boundingRect(contour)
area_text = f"Area: {int(area)}"
ratio_text = f"Ratio: {ratio:.2f}" if ratio != float('inf') else "Ratio: inf"
# 计算文本尺寸
ratio_size = cv2.getTextSize(ratio_text, font, font_scale, thickness)[0]
area_size = cv2.getTextSize(area_text, font, font_scale, thickness)[0]
max_width = max(ratio_size[0], area_size[0])
total_height = ratio_size[1] + area_size[1] + 10
# 确定文本位置
if y > total_height + 10: # 顶部放置
ratio_pos = (x, y - 10)
area_pos = (x, y - ratio_size[1] - 20)
bg_top = area_pos[1] - 5
bg_bottom = ratio_pos[1] + 5
else: # 底部放置
ratio_pos = (x, y + h + 30)
area_pos = (x, y + h + 30 + ratio_size[1] + 10)
bg_top = ratio_pos[1] - ratio_size[1] - 5
bg_bottom = area_pos[1] + 5
# 绘制文本背景
cv2.rectangle(img, (x, bg_top), (x + max_width + 5, bg_bottom), (0, 0, 0), -1)
# 绘制文本
cv2.putText(img, ratio_text, ratio_pos, font, font_scale, ratio_color, thickness, cv2.LINE_AA)
cv2.putText(img, area_text, area_pos, font, font_scale, area_color, thickness, cv2.LINE_AA)
def _draw_black_region_info(img, black_regions):
"""在图像上绘制黑色区域的轮廓和面积信息"""
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.7
color = (0, 255, 255) # 黄色
contour_color = (0, 255, 0) # 绿色
thickness = 2
for region in black_regions:
cnt = region["contour"]
area = region["area"]
# 绘制黑色区域轮廓
cv2.drawContours(img, [cnt], -1, contour_color, 2)
# 准备面积文本
x, y, w, h = cv2.boundingRect(cnt)
text = f"Black: {int(area)}"
text_size = cv2.getTextSize(text, font, font_scale, thickness)[0]
# 确定文本位置
if y > 20: # 上方放置
text_pos = (x, y - 10)
bg_top = text_pos[1] - text_size[1] - 5
bg_bottom = text_pos[1] + 5
else: # 下方放置
text_pos = (x, y + h + 20)
bg_top = text_pos[1] - text_size[1] - 5
bg_bottom = text_pos[1] + 5
# 绘制文本背景和文本
cv2.rectangle(img, (x, bg_top), (x + text_size[0] + 5, bg_bottom), (0, 0, 0), -1)
cv2.putText(img, text, text_pos, font, font_scale, color, thickness, cv2.LINE_AA)
def _save_result_image(img_path, img):
"""保存处理后的图像并返回保存路径"""
file_dir, file_name = os.path.split(img_path)
name, ext = os.path.splitext(file_name)
save_path = os.path.join(file_dir, f"{name}_contour{ext}")
cv2.imwrite(save_path, img)
return save_path
def calculate_contour_ratios(img_path):
"""
分析图像中的轮廓,计算每个轮廓的面积和白黑像素比值,绘制轮廓并标注后保存
参数:
img_path: 图像文件的路径
返回:
列表,包含每个符合条件的轮廓的(面积, 白像素/黑像素比值)元组
若图像读取失败,返回空列表
"""
# 预处理图像
img, gray, binary = _preprocess_image(img_path)
if img is None:
print(f"无法读取图像,请检查路径是否正确:{img_path}")
return []
# 提取大轮廓
filtered_contours = _extract_large_contours(binary)
if not filtered_contours:
print(f"图像 {img_path} 未检测到符合条件的大轮廓")
return []
# 准备绘制
img_with_contours = img.copy()
result_list = []
# 处理每个大轮廓
for cnt in filtered_contours:
# 计算大轮廓面积和白黑比
area = cv2.contourArea(cnt)
mask = np.zeros_like(binary)
cv2.drawContours(mask, [cnt], -1, 255, -1)
white_pixels = cv2.countNonZero(cv2.bitwise_and(binary, mask))
total_pixels = cv2.countNonZero(mask)
black_pixels = total_pixels - white_pixels
ratio = white_pixels / black_pixels if black_pixels != 0 else float('inf')
# 存储结果
result_list.append((area, ratio))
# 绘制大轮廓信息
_draw_large_contour_info(img_with_contours, cnt, area, ratio)
# 处理并绘制黑色区域
black_regions = _process_black_regions(cnt, binary)
_draw_black_region_info(img_with_contours, black_regions)
# 绘制所有大轮廓
cv2.drawContours(img_with_contours, filtered_contours, -1, (0, 0, 255), 2)
# 保存结果图像
save_path = _save_result_image(img_path, img_with_contours)
print(f"带轮廓、面积和比值的图像已保存至:{save_path}")
return result_list
# 使用示例
if __name__ == "__main__":
image_paths = ["D:\\img\\test3\\1.jpg",
"D:\\img\\test3\\2.jpg",
"D:\\img\\test3\\3.jpg",
"D:\\img\\test3\\4.jpg",
"D:\\img\\test3\\5.jpg",
"D:\\img\\test3\\6.jpg",
"D:\\img\\test3\\7.jpg",
"D:\\img\\test3\\8.jpg",
"D:\\img\\test3\\9.jpg",
"D:\\img\\test3\\10.jpg"]
all_results = []
for path in image_paths:
print(f"处理图像: {path}")
results = calculate_contour_ratios(path)
if results:
print(f" 检测到 {len(results)} 个轮廓")
for i, (area, ratio) in enumerate(results, 1):
print(f" 轮廓 {i}: 面积={area:.0f}, 对比度={ratio:.4f}")
all_results.extend(results)
else:
print(" 未检测到符合条件的轮廓或图像读取失败")
print("\n所有图像的轮廓信息汇总:")
print(f"总共有 {len(all_results)} 个轮廓")
for i, (area, ratio) in enumerate(all_results, 1):
print(f" 轮廓 {i}: 面积={area:.0f}, 对比度={ratio:.4f}")
代码优化
import cv2
import numpy as np
import os
def _preprocess_image(img_path , threshold):
"""预处理图像:读取、转灰度、二值化和形态学操作"""
# 读取图像
img = cv2.imread(img_path)
if img is None:
return None, None, None
# 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 阈值分割(提取白色区域)
ret, binary = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY)
# 形态学操作:增强白色区域连通度
kernel = np.ones((7, 7), np.uint8)
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=1)
closing = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel, iterations=3)
return img, gray, closing
def _extract_large_contours(binary_img, min_area=105000):
"""提取并过滤出面积足够大的轮廓"""
contours, hierarchy = cv2.findContours(
binary_img,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
return [cnt for cnt in contours if cv2.contourArea(cnt) >= min_area]
def _process_black_regions(contour, binary_img , area):
"""处理单个大轮廓内的黑色区域,返回符合条件的黑色区域信息"""
# 创建轮廓掩码
mask = np.zeros_like(binary_img)
cv2.drawContours(mask, [contour], -1, 255, -1)
# 提取轮廓内的区域并反转以突出黑色区域
contour_region = cv2.bitwise_and(binary_img, mask)
black_regions = cv2.bitwise_not(contour_region)
# 阈值处理确保纯黑白
_, black_binary = cv2.threshold(black_regions, 127, 255, cv2.THRESH_BINARY)
# 形态学操作:断开细小连接
small_kernel = np.ones((7, 7), np.uint8)
eroded = cv2.erode(black_binary, small_kernel, iterations=3)
dilated = cv2.dilate(eroded, small_kernel, iterations=1)
dilated = cv2.bitwise_and(dilated, mask) # 确保在大轮廓内
# 查找黑色区域轮廓
black_contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 过滤并计算面积
min_black_area = area
return [
{"contour": cnt, "area": cv2.contourArea(cnt)}
for cnt in black_contours
if cv2.contourArea(cnt) >= min_black_area
]
def _draw_large_contour_info(img, contour, area, ratio):
"""在图像上绘制大轮廓的面积和比值信息"""
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.7
ratio_color = (0, 255, 0)
area_color = (255, 0, 0)
thickness = 2
x, y, w, h = cv2.boundingRect(contour)
area_text = f"Area: {int(area)}"
ratio_text = f"Ratio: {ratio:.2f}" if ratio != float('inf') else "Ratio: inf"
# 计算文本尺寸
ratio_size = cv2.getTextSize(ratio_text, font, font_scale, thickness)[0]
area_size = cv2.getTextSize(area_text, font, font_scale, thickness)[0]
max_width = max(ratio_size[0], area_size[0])
total_height = ratio_size[1] + area_size[1] + 10
# 确定文本位置
if y > total_height + 10: # 顶部放置
ratio_pos = (x, y - 10)
area_pos = (x, y - ratio_size[1] - 20)
bg_top = area_pos[1] - 5
bg_bottom = ratio_pos[1] + 5
else: # 底部放置
ratio_pos = (x, y + h + 30)
area_pos = (x, y + h + 30 + ratio_size[1] + 10)
bg_top = ratio_pos[1] - ratio_size[1] - 5
bg_bottom = area_pos[1] + 5
# 绘制文本背景
cv2.rectangle(img, (x, bg_top), (x + max_width + 5, bg_bottom), (0, 0, 0), -1)
# 绘制文本
cv2.putText(img, ratio_text, ratio_pos, font, font_scale, ratio_color, thickness, cv2.LINE_AA)
cv2.putText(img, area_text, area_pos, font, font_scale, area_color, thickness, cv2.LINE_AA)
def _draw_black_region_info(img, black_regions):
"""在图像上绘制黑色区域的轮廓和面积信息"""
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.7
color = (0, 255, 255) # 黄色
contour_color = (0, 255, 0) # 绿色
thickness = 2
for region in black_regions:
cnt = region["contour"]
area = region["area"]
# 绘制黑色区域轮廓
cv2.drawContours(img, [cnt], -1, contour_color, 2)
# 准备面积文本
x, y, w, h = cv2.boundingRect(cnt)
text = f"Black: {int(area)}"
text_size = cv2.getTextSize(text, font, font_scale, thickness)[0]
# 确定文本位置
if y > 20: # 上方放置
text_pos = (x, y - 10)
bg_top = text_pos[1] - text_size[1] - 5
bg_bottom = text_pos[1] + 5
else: # 下方放置
text_pos = (x, y + h + 20)
bg_top = text_pos[1] - text_size[1] - 5
bg_bottom = text_pos[1] + 5
# 绘制文本背景和文本
cv2.rectangle(img, (x, bg_top), (x + text_size[0] + 5, bg_bottom), (0, 0, 0), -1)
cv2.putText(img, text, text_pos, font, font_scale, color, thickness, cv2.LINE_AA)
def _save_result_image(img_path, img):
"""保存处理后的图像并返回保存路径"""
file_dir, file_name = os.path.split(img_path)
name, ext = os.path.splitext(file_name)
save_path = os.path.join(file_dir, f"{name}_contour{ext}")
cv2.imwrite(save_path, img)
return save_path
def calculate_contour_ratios(img_path , threshold , area1 , area2):
"""
分析图像中的轮廓,计算每个轮廓的面积和白黑像素比值,绘制轮廓并标注后保存
参数:
img_path: 图像文件的路径
返回:
列表,包含每个符合条件的轮廓的(面积, 白像素/黑像素比值)元组
若图像读取失败,返回空列表
"""
# 预处理图像
img, gray, binary = _preprocess_image(img_path , threshold)
if img is None:
print(f"无法读取图像,请检查路径是否正确:{img_path}")
return []
# 提取大轮廓
filtered_contours = _extract_large_contours(binary ,area1 )
if not filtered_contours:
print(f"图像 {img_path} 未检测到符合条件的大轮廓")
return []
# 准备绘制
img_with_contours = img.copy()
result_list = []
black_list =[]
# 处理每个大轮廓
for cnt in filtered_contours:
# 计算大轮廓面积和白黑比
area = cv2.contourArea(cnt)
mask = np.zeros_like(binary)
cv2.drawContours(mask, [cnt], -1, 255, -1)
white_pixels = cv2.countNonZero(cv2.bitwise_and(binary, mask))
total_pixels = cv2.countNonZero(mask)
black_pixels = total_pixels - white_pixels
ratio = white_pixels / black_pixels if black_pixels != 0 else float('inf')
# 存储结果
result_list.append((area, ratio))
# 绘制大轮廓信息
_draw_large_contour_info(img_with_contours, cnt, area, ratio)
# 处理并绘制黑色区域
black_regions = _process_black_regions(cnt, binary , area2)
black_list.extend(black_regions)
_draw_black_region_info(img_with_contours, black_regions)
# 绘制所有大轮廓
cv2.drawContours(img_with_contours, filtered_contours, -1, (0, 0, 255), 2)
# 保存结果图像
save_path = _save_result_image(img_path, img_with_contours)
print(f"带轮廓、面积和比值的图像已保存至:{save_path}")
return result_list , black_list
# 使用示例
if __name__ == "__main__":
image_paths = ["D:\\img\\test3\\1.jpg",
"D:\\img\\test3\\2.jpg",
"D:\\img\\test3\\3.jpg",
"D:\\img\\test3\\4.jpg",
"D:\\img\\test3\\5.jpg",
"D:\\img\\test3\\6.jpg",
"D:\\img\\test3\\7.jpg",
"D:\\img\\test3\\8.jpg",
"D:\\img\\test3\\9.jpg",
"D:\\img\\test3\\10.jpg"]
all_results = []
all_results_black = []
for path in image_paths:
print(f"处理图像: {path}")
results,result_black = calculate_contour_ratios(path,20,105000,3000)
if results:
print(f" 检测到 {len(results)} 个轮廓")
for i, (area, ratio) in enumerate(results, 1):
print(f" 轮廓 {i}: 面积={area:.0f}, 对比度={ratio:.4f}")
all_results.extend(results)
else:
print(" 未检测到符合条件的轮廓或图像读取失败")
print(result_black)
all_results_black.extend(result_black)
print("\n所有图像的轮廓信息汇总:")
print("找到黑色异物个数"+str(len(all_results_black)))
print(f"总共有 {len(all_results)} 个轮廓")
for i, (area, ratio) in enumerate(all_results, 1):
print(f" 轮廓 {i}: 面积={area:.0f}, 对比度={ratio:.4f}")

1127

被折叠的 条评论
为什么被折叠?



