本節觀點:一個好的提問就已經解決了問題的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 shutildef read_image_chinese_path(image_path):# 支持中文路徑讀取image_data = np.fromfile(image_path, dtype=np.uint8)img = cv2.imdecode(image_data, cv2.IMREAD_COLOR)return imgdef 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 Falseheight, 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 = 0for 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 += 1break # 這條輪廓計數一次就夠return edge_touch_count >= edge_touch_thresholddef 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 = 1while 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 shutildef read_image_chinese_path(image_path):"""支持中文路徑讀取"""image_data = np.fromfile(image_path, dtype=np.uint8)img = cv2.imdecode(image_data, cv2.IMREAD_COLOR)return imgdef 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 orx + w >= width - edge_proximity ory + 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 Falsedef 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 Falseheight, 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 = 0for 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 += 1break# 條件1:足夠多的觸邊線條if edge_touch_count < edge_touch_threshold:return False# 條件2:是否有色塊兩側有邊緣if not has_large_colored_block_with_edges(img, block_size):return Falsereturn 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 = 1while 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 文件夾")