2025-07-15通过边缘线检测图像里的主体有没有出血

本节观点:一个好的提问就已经解决了问题的90%。

对于问题的描述正确与否决定了解决问题的方法和路径,所以我们在AI时代必须要学会正确的描述问题和表达问题,否则即使有AI辅助也是很难精准的解决问题。

我的问题:

如何利用代码从图片集中筛选出出血的图片,定义出血:提取图片中的边缘,我希望通过设置一个值来界定,比如所有边缘线与图像边缘接触的总条数超过10条才算出血图片,另外图像中的边缘线段至少长20像素才计入有效边缘线段,同时要考虑图片路径含有中文的问题。

如下图所示,其中1,2是出血的图,3,4是没有出血的图。通过上方的描述提交给AI,AI就能帮我们实现批量筛选所有出血的图片。

特别说明:

国产AI水平确实远低于国外的,同样的问题,GPT一次就搞定了代码,而豆包,qwen,deepseek等从多国产AI提问了N轮最后连最基本的读图时中文路径问题都无法搞定,可笑至极。加油国产AI。

复制代码
import cv2
import numpy as np
import os
import shutil

def read_image_chinese_path(image_path):
    # 支持中文路径读取
    image_data = np.fromfile(image_path, dtype=np.uint8)
    img = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
    return img

def is_bleeding_image(image_path, edge_touch_threshold=10, min_edge_length=20):
    img = read_image_chinese_path(image_path)
    if img is None:
        print(f"无法读取图片: {image_path}")
        return False

    height, width = img.shape[:2]

    # 灰度 + Canny 边缘检测
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 100, 200)

    # 查找轮廓
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    edge_touch_count = 0
    for cnt in contours:
        # 计算轮廓长度
        length = cv2.arcLength(cnt, closed=False)
        if length < min_edge_length:
            continue  # 过滤掉短轮廓

        # 检查是否有点在图像边界
        for point in cnt:
            x, y = point[0]
            if x <= 0 or y <= 0 or x >= width-1 or y >= height-1:
                edge_touch_count += 1
                break  # 这条轮廓计数一次就够

    return edge_touch_count >= edge_touch_threshold

def find_and_move_bleeding_images(folder_path, output_folder, edge_touch_threshold=10, min_edge_length=20):
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp')):
                file_path = os.path.join(root, file)
                if is_bleeding_image(file_path, edge_touch_threshold, min_edge_length):
                    print(f"检测到出血: {file_path}")

                    # 生成输出路径
                    output_path = os.path.join(output_folder, file)
                    # 如果重名文件,改名避免覆盖
                    base, ext = os.path.splitext(output_path)
                    counter = 1
                    while os.path.exists(output_path):
                        output_path = f"{base}_{counter}{ext}"
                        counter += 1
                    # 移动文件
                    shutil.move(file_path, output_path)

if __name__ == "__main__":
    input_folder = r"images"         # 输入图片文件夹路径
    output_folder = r"output_images"    # 检测到出血后保存到这里

    find_and_move_bleeding_images(input_folder, output_folder,
                                  edge_touch_threshold=10,
                                  min_edge_length=20)

    print("✅ 所有出血图片已移动至 output_images 文件夹")

最后描述问题过程:

第1次:

我有几张图片,其中有一张图片内容是非常完整的,主体在图的中间(这里要考虑图片的背景,有的图片背景是纯白色,有的图片是纯黑色,有的图片的是其它纯色的背景),而其余的图片都是主体出血。如何利用代码检测或筛选出主体没𠕇出血的图片
出来的代码只能做到60%选对。

第2次:

重新整理一下思路:1,分别取出图像外边1像素宽的边缘,2.分别计算每边的像素色值是否一样,如果计算出有明显不同的像素色值部分的像素总计超过5%单边所有像素色值比例的说明是出血图像。
出来的代码只能做到65%选对。

第3次:

如何利用代码从图片集中筛选出出血的图片,定义出血:提取图片中的边缘,我希望通过设置一个值来界定,比如所有边缘线与图像边缘接触的总条数超过10条才算出血图片,另外图像中的边缘线段至少长20像素才计入有效边缘线段,同时要考虑图片路径含有中文的问题。
出来的代码只能做到95%选对。

最终代码

bash 复制代码
import cv2
import numpy as np
import os
import shutil

def read_image_chinese_path(image_path):
    """支持中文路径读取"""
    image_data = np.fromfile(image_path, dtype=np.uint8)
    img = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
    return img

def has_large_colored_block_with_edges(img, block_size=10, edge_proximity=5):
    """
    检查是否存在大于 block_size 的色块
    且色块两侧有边缘线,并且色块接近图像边缘
    """
    height, width = img.shape[:2]

    # 边缘检测
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 100, 200)

    # 查找轮廓
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        # 判断色块尺寸是否足够大
        if w >= block_size and h >= block_size:
            # 检查色块是否靠近图像边缘
            if (x <= edge_proximity or y <= edge_proximity or
                x + w >= width - edge_proximity or
                y + h >= height - edge_proximity):

                # 检查 ROI 内边缘线
                roi_edges = edges[y:y+h, x:x+w]
                top_edge = np.sum(roi_edges[0, :] > 0)
                bottom_edge = np.sum(roi_edges[-1, :] > 0)
                left_edge = np.sum(roi_edges[:, 0] > 0)
                right_edge = np.sum(roi_edges[:, -1] > 0)

                edge_threshold = 3  # 至少3个像素视作边缘线

                if (left_edge >= edge_threshold and right_edge >= edge_threshold):
                    return True  # 找到了符合要求的色块

    return False


def is_bleeding_image(image_path, edge_touch_threshold=10, min_edge_length=20, block_size=10):
    img = read_image_chinese_path(image_path)
    if img is None:
        print(f"无法读取图片: {image_path}")
        return False

    height, width = img.shape[:2]

    # 灰度 + 边缘检测
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 100, 200)

    # 查找所有外轮廓
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    edge_touch_count = 0
    for cnt in contours:
        # 计算轮廓长度
        length = cv2.arcLength(cnt, closed=False)
        if length < min_edge_length:
            continue

        # 检查是否触碰图像边界
        for point in cnt:
            x, y = point[0]
            if x <= 0 or y <= 0 or x >= width-1 or y >= height-1:
                edge_touch_count += 1
                break

    # 条件1:足够多的触边线条
    if edge_touch_count < edge_touch_threshold:
        return False

    # 条件2:是否有色块两侧有边缘
    if not has_large_colored_block_with_edges(img, block_size):
        return False

    return True  # 两个条件都满足

def find_and_move_bleeding_images(folder_path, output_folder, edge_touch_threshold=10, min_edge_length=20, block_size=10):
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp')):
                file_path = os.path.join(root, file)
                if is_bleeding_image(file_path, edge_touch_threshold, min_edge_length, block_size):
                    print(f"检测到出血: {file_path}")

                    # 生成输出路径
                    output_path = os.path.join(output_folder, file)
                    base, ext = os.path.splitext(output_path)
                    counter = 1
                    while os.path.exists(output_path):
                        output_path = f"{base}_{counter}{ext}"
                        counter += 1

                    # 移动文件
                    shutil.move(file_path, output_path)

if __name__ == "__main__":
    input_folder = r"images"         # 输入文件夹路径
    output_folder = r"output_images"    # 出血图片保存路径

    find_and_move_bleeding_images(
        input_folder, output_folder,
        edge_touch_threshold=1,    # 触边线条数阈值
        min_edge_length=2,         # 有效边缘线长度
        block_size=2               # 色块尺寸阈值
    )

    print("✅ 所有出血图片已移动至 output_images 文件夹")


相关推荐
HuggingFace1 小时前
Hugging Face 开源 HopeJR 机器臂!今天晚上直播带你深入技术核心
人工智能
SUPER52662 小时前
AI应用服务
人工智能
义薄云天us2 小时前
028_分布式部署架构
人工智能·分布式·架构·claude code
HuggingFace3 小时前
HF Papers 直播| AI for Science 专场
人工智能
机器视觉与AI3 小时前
半导体制造流程深度解析:外观缺陷检测的AI化路径与实践
人工智能·视觉检测·制造
zyhomepage6 小时前
科技的成就(六十九)
开发语言·网络·人工智能·科技·内容运营
停走的风6 小时前
(李宏毅)deep learning(五)--learning rate
人工智能·深度学习·机器学习
fishjar1007 小时前
LLaMA-Factory安装部署
人工智能·深度学习
feifeikon7 小时前
模型篇(Bert llama deepseek)
人工智能·深度学习·自然语言处理