import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
import os
import time
# 解决matplotlib中文显示问题
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False
def adaptive_lighting_correction(image):
"""自适应光照校正 - 修改为使用Otsu阈值化"""
if len(image.shape) == 3:
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
else:
gray = image.copy()
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
clahe_img = clahe.apply(gray)
# 将自适应阈值改为Otsu阈值
ret, otsu_thresh = cv.threshold(clahe_img, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
return clahe_img, otsu_thresh
def morphological_processing(binary_img):
"""对二值图像进行形态学处理"""
# 创建结构元素
kernel_ellipse = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3, 3))
kernel_rect = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
# 开运算去除小噪声
opening = cv.morphologyEx(binary_img, cv.MORPH_OPEN, kernel_ellipse, iterations=2)
# 闭运算填充小孔洞
closing = cv.morphologyEx(opening, cv.MORPH_CLOSE, kernel_ellipse, iterations=2)
# 膨胀操作连接断裂区域
dilation = cv.dilate(closing, kernel_rect, iterations=1)
return dilation, opening, closing
def skeletonize_image(binary_img):
"""图像骨化(骨架提取)技术"""
# 创建一个用于骨架化的空图像
skeleton = np.zeros(binary_img.shape, np.uint8)
# 获取结构元素
kernel = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3))
# 循环直到没有更多的像素可以腐蚀
while True:
# 开运算
opened = cv.morphologyEx(binary_img, cv.MORPH_OPEN, kernel)
# 从原图中减去开运算结果
temp = cv.subtract(binary_img, opened)
# 对原图进行腐蚀
eroded = cv.erode(binary_img, kernel)
# 骨架化结果
skeleton = cv.bitwise_or(skeleton, temp)
# 更新原图
binary_img = eroded.copy()
# 如果没有白色像素了,退出循环
if cv.countNonZero(binary_img) == 0:
break
return skeleton
def improved_marker_based_watershed(image, binary_img):
"""改进的基于标记的分水岭算法 - 使用骨化技术"""
# 开运算去除小噪声
kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3, 3))
opening = cv.morphologyEx(binary_img, cv.MORPH_OPEN, kernel, iterations=1)
# 闭运算填充小孔洞
closing = cv.morphologyEx(opening, cv.MORPH_CLOSE, kernel, iterations=1)
# 确定背景区域
sure_bg = cv.dilate(closing, kernel, iterations=2)
# 使用骨化技术处理图像
skeleton = skeletonize_image(closing)
# 在骨化图像上进行距离变换
dist_transform = cv.distanceTransform(skeleton, cv.DIST_L2, 5)
dist_display = cv.normalize(dist_transform, None, 0, 1.0, cv.NORM_MINMAX)
# 确定前景区域
threshold_ratio = 0.3
ret, sure_fg = cv.threshold(dist_transform,
threshold_ratio * dist_transform.max(),
255, 0)
sure_fg = np.uint8(sure_fg)
# 找到未知区域
unknown = cv.subtract(sure_bg, sure_fg)
# 标记连通组件
ret, markers = cv.connectedComponents(sure_fg)
markers = markers + 1
markers[unknown == 255] = 0
# 应用分水岭算法
markers = cv.watershed(image, markers)
return markers, dist_transform, sure_fg, sure_bg, dist_display, skeleton
def process_gravel_image_method1(image_path):
"""方案一:使用双边滤波、形态学处理和骨化技术"""
print("开始执行方案一...")
start_time = time.time()
# 读取和预处理图像
img1 = cv.imread(image_path)
if img1 is None:
print(f"无法读取图像: {image_path}")
return None, 0, 0
img = cv.resize(img1, (640, 400))
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 自适应光照校正(现在使用Otsu阈值化)
clahe_img, otsu_bin = adaptive_lighting_correction(gray_img)
# 由于现在adaptive_lighting_correction已经返回Otsu阈值结果,直接使用它
bin_img = otsu_bin
method_name = "Otsu阈值"
# 使用双边滤波替代中值滤波
# 注意:双边滤波需要彩色图像,所以我们在灰度图像上使用,但需要转换为三通道
if len(img.shape) == 3:
bilateral_img = cv.bilateralFilter(img, d=9, sigmaColor=75, sigmaSpace=75)
bilateral_gray = cv.cvtColor(bilateral_img, cv.COLOR_BGR2GRAY)
else:
# 如果是灰度图,先转换为三通道再处理
img_3channel = cv.cvtColor(img, cv.COLOR_GRAY2BGR)
bilateral_img = cv.bilateralFilter(img_3channel, d=9, sigmaColor=75, sigmaSpace=75)
bilateral_gray = cv.cvtColor(bilateral_img, cv.COLOR_BGR2GRAY)
# 对双边滤波后的图像进行二值化
ret, bilateral_bin = cv.threshold(bilateral_gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
# 对二值化后的图像进行形态学处理
morph_processed, morph_open, morph_close = morphological_processing(bilateral_bin)
# 应用改进的分水岭算法(使用骨化技术)
markers, dist_transform, sure_fg, sure_bg, dist_display, skeleton = improved_marker_based_watershed(img,
morph_processed)
# 创建结果图像
result_img = img.copy()
watershed_boundary_img = img.copy()
watershed_boundary_img[markers == -1] = [0, 0, 255]
# 处理每个分割区域
unique_markers = np.unique(markers)
particle_count = 0
valid_particles = []
areas = []
for marker in unique_markers:
if marker > 1:
mask = np.uint8(markers == marker)
contours, _ = cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
if contours:
contour = contours[0]
area = cv.contourArea(contour)
areas.append(area)
if 30 < area < 10000:
particle_count += 1
valid_particles.append(contour)
cv.drawContours(result_img, [contour], -1, (0, 255, 0), 2)
M = cv.moments(contour)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
cv.putText(result_img, str(particle_count), (cx, cy),
cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
# 计算统计信息
end_time = time.time()
processing_time = end_time - start_time
if areas:
avg_area = np.mean(areas)
max_area = np.max(areas)
min_area = np.min(areas)
else:
avg_area = max_area = min_area = 0
# 可视化结果
plt.figure(figsize=(20, 15))
plots = [
(cv.cvtColor(img, cv.COLOR_BGR2RGB), "原图"),
(gray_img, "灰度图", 'gray'),
(clahe_img, "CLAHE增强", 'gray'),
(bin_img, f"直接二值化 ({method_name})", 'gray'),
(bilateral_gray, "双边滤波后", 'gray'),
(bilateral_bin, "双边滤波后二值化", 'gray'),
(morph_open, "形态学开运算", 'gray'),
(morph_close, "形态学闭运算", 'gray'),
(morph_processed, "形态学处理后", 'gray'),
(skeleton, "骨化图像", 'gray'),
(dist_display, "距离变换", 'jet'),
(sure_fg, "确定前景", 'gray'),
(cv.cvtColor(watershed_boundary_img, cv.COLOR_BGR2RGB), "分水岭边界"),
(cv.cvtColor(result_img, cv.COLOR_BGR2RGB), f"最终结果 (检测到{particle_count}个颗粒)")
]
for i, plot_data in enumerate(plots, 1):
plt.subplot(4, 4, i)
if len(plot_data) == 3:
plt.imshow(plot_data[0], cmap=plot_data[2])
else:
plt.imshow(plot_data[0])
plt.title(plot_data[1])
plt.axis('off')
plt.tight_layout()
plt.show()
# 打印详细结果
print("\n" + "=" * 60)
print("方案一结果报告")
print("=" * 60)
print(f"检测到的颗粒数量: {particle_count}")
print(f"使用的二值化方法: {method_name}")
print(f"滤波方法: 双边滤波")
print(f"形态学处理: 开运算+闭运算+膨胀")
print(f"距离变换预处理: 图像骨化技术")
print(f"处理时间: {processing_time:.2f} 秒")
print(f"颗粒面积统计:")
print(f" - 平均面积: {avg_area:.2f} 像素")
print(f" - 最大面积: {max_area:.2f} 像素")
print(f" - 最小面积: {min_area:.2f} 像素")
print(f"标记区域数量: {len(unique_markers) - 2}") # 减去背景和边界
print("=" * 60)
return result_img, particle_count, processing_time
# 添加主函数调用
def main():
# 替换为您的图像路径
img_file = r"E:\code\PythonProjectfuxian\image\gravel_image1.jpg"
# 检查文件是否存在
if not os.path.exists(img_file):
print(f"图像文件不存在: {img_file}")
print("请检查文件路径是否正确")
return
# 调用方案一处理函数
result, count, time_used = process_gravel_image_method1(img_file)
# 打印最终总结
print("\n" + "=" * 60)
print("处理完成总结")
print("=" * 60)
print(f"总共检测到 {count} 个颗粒")
print(f"总处理时间: {time_used:.2f} 秒")
print("=" * 60)
# 确保主函数被调用
if __name__ == "__main__":
main()

一段基于标记的分水岭算法的改进
- 写回答
- 好问题 0 提建议
- 关注问题
- 邀请回答
-
3条回答 默认 最新
关注让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。
如果答案让您满意,请采纳、点赞、关注,非常感谢!
.
每日一句正能量,让它带给您好心情:每一个不曾放弃的瞬间,都在为未来积蓄力量。
.基于标记的分水岭算法改进分析
这段代码实现了一个改进的基于标记的分水岭算法,主要用于颗粒图像的分割和计数。以下是对代码的分析和改进建议:
主要改进点
1. 自适应光照校正优化
def adaptive_lighting_correction(image): """自适应光照校正 - 修改为使用Otsu阈值化""" if len(image.shape) == 3: gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) else: gray = image.copy() clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) clahe_img = clahe.apply(gray) # 将自适应阈值改为Otsu阈值 ret, otsu_thresh = cv.threshold(clahe_img, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU) return clahe_img, otsu_thresh改进效果:使用Otsu阈值化替代传统自适应阈值,能更好地处理光照不均匀的图像。
2. 骨化技术应用
def skeletonize_image(binary_img): """图像骨化(骨架提取)技术""" skeleton = np.zeros(binary_img.shape, np.uint8) kernel = cv.getStructuringElement(cv.MORPH_CROSS, (3, 3)) while True: opened = cv.morphologyEx(binary_img, cv.MORPH_OPEN, kernel) temp = cv.subtract(binary_img, opened) eroded = cv.erode(binary_img, kernel) skeleton = cv.bitwise_or(skeleton, temp) binary_img = eroded.copy() if cv.countNonZero(binary_img) == 0: break return skeleton优势:骨化技术能有效提取颗粒的骨架结构,为后续距离变换提供更好的输入。
3. 改进的分水岭算法
def improved_marker_based_watershed(image, binary_img): """改进的基于标记的分水岭算法 - 使用骨化技术""" # 使用骨化技术处理图像 skeleton = skeletonize_image(closing) # 在骨化图像上进行距离变换 dist_transform = cv.distanceTransform(skeleton, cv.DIST_L2, 5) # 确定前景区域 threshold_ratio = 0.3 ret, sure_fg = cv.threshold(dist_transform, threshold_ratio * dist_transform.max(), 255, 0)创新点:将骨化技术与距离变换结合,更准确地确定前景标记。
代码优化建议
1. 参数可配置化
建议将关键参数提取为函数参数或配置文件:
class WatershedConfig: def __init__(self): self.clahe_clip_limit = 2.0 self.clahe_grid_size = (8, 8) self.bilateral_d = 9 self.bilateral_sigma_color = 75 self.bilateral_sigma_space = 75 self.threshold_ratio = 0.3 self.min_particle_area = 30 self.max_particle_area = 100002. 错误处理增强
def process_gravel_image_method1(image_path): """方案一:使用双边滤波、形态学处理和骨化技术""" try: img1 = cv.imread(image_path) if img1 is None: raise FileNotFoundError(f"无法读取图像: {image_path}") # 处理逻辑... except Exception as e: print(f"处理图像时发生错误: {e}") return None, 0, 03. 性能优化建议
# 使用多进程处理大图像 from multiprocessing import Pool def process_image_parallel(image_paths): with Pool(processes=4) as pool: results = pool.map(process_gravel_image_method1, image_paths) return results算法优势
- 抗干扰能力强:双边滤波有效保留边缘同时去除噪声
- 分割精度高:骨化技术+距离变换提供准确的前景标记
- 适应性好:CLAHE光照校正处理不同光照条件
- 可视化完善:完整的处理流程可视化便于调试
潜在改进方向
- 深度学习集成:结合U-Net等深度学习模型进行初始分割
- 多尺度处理:针对不同大小的颗粒采用多尺度策略
- 3D扩展:适用于三维颗粒图像分析
- 实时处理:优化算法实现实时颗粒检测
这个改进方案在传统图像处理基础上融入了骨化技术,显著提升了分水岭算法的分割精度和鲁棒性。
解决 无用评论 打赏 举报