本节观点:一个好的提问就已经解决了问题的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%选对。
最终代码
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 文件夹")