給定普通有色影像找到特定區域

little_cute發表於2024-08-13
` import os import time import cv2 import numpy as np import math import datetime def filter_rectangles(rect_list, min_area_threshold, width_to_height_ratio_thresh=3): filtered_rectangles = [] for rect in rect_list: width = rect['width'] height = rect['height'] area = width * height if area >= min_area_threshold: if width / height <= width_to_height_ratio_thresh: filtered_rectangles.append(rect) return filtered_rectangles def merge_intersecting_rectangles(rect_info_list, merge_thresh=20): rectangles = [] for rect_info in rect_info_list: x = rect_info['x'] y = rect_info['y'] width = rect_info['width'] height = rect_info['height'] rectangles.append((x, y, width, height)) merged_rectangles = [] for rect in rectangles: x, y, width, height = rect rect_cv = (x, y, width, height) if merged_rectangles: merge_attempted = False for i, merged_rect in enumerate(merged_rectangles): x_intersect = max(rect_cv[0], merged_rect[0]) y_intersect = max(rect_cv[1], merged_rect[1]) x2_intersect = min(rect_cv[0] + rect_cv[2], merged_rect[0] + merged_rect[2]) y2_intersect = min(rect_cv[1] + rect_cv[3], merged_rect[1] + merged_rect[3]) if x_intersect < x2_intersect and y_intersect < y2_intersect: intersection_area = (x2_intersect - x_intersect) * (y2_intersect - y_intersect) rect1_area = rect_cv[2] * rect_cv[3] rect2_area = merged_rect[2] * merged_rect[3] union_area = rect1_area + rect2_area - intersection_area iou = intersection_area / union_area if iou > 0.0: x = min(rect_cv[0], merged_rect[0]) y = min(rect_cv[1], merged_rect[1]) w = max(rect_cv[0] + rect_cv[2], merged_rect[0] + merged_rect[2]) - x h = max(rect_cv[1] + rect_cv[3], merged_rect[1] + merged_rect[3]) - y merged_rectangles[i] = (x, y, w, h) merge_attempted = True break elif (abs(rect_cv[0] - merged_rect[0]) < merge_thresh and abs(rect_cv[1] - merged_rect[1]) < merge_thresh): x = min(rect_cv[0], merged_rect[0]) y = min(rect_cv[1], merged_rect[1]) w = max(rect_cv[0] + rect_cv[2], merged_rect[0] + merged_rect[2]) - x h = max(rect_cv[1] + rect_cv[3], merged_rect[1] + merged_rect[3]) - y merged_rectangles[i] = (x, y, w, h) merge_attempted = True break if not merge_attempted: merged_rectangles.append(rect_cv) else: merged_rectangles.append(rect_cv) merged_rect_info_list = [{'x': r[0], 'y': r[1], 'width': r[2], 'height': r[3]} for r in merged_rectangles] return merged_rect_info_list def merge_close_rectangles_by_linbian(rect_info_list, merge_thresh=20): rectangles = [] for rect_info in rect_info_list: x = rect_info['x'] y = rect_info['y'] width = rect_info['width'] height = rect_info['height'] rectangles.append((x, y, width, height)) merged_rectangles = [] for rect in rectangles: x, y, width, height = rect rect_cv = (x, y, width, height) if merged_rectangles: merge_attempted = False for i, merged_rect in enumerate(merged_rectangles): if abs(rect_cv[1] - (merged_rect[1] + merged_rect[3])) < merge_thresh: if (rect_cv[0] < merged_rect[0] + merged_rect[2] and merged_rect[0] < rect_cv[0] + rect_cv[2]): x = min(rect_cv[0], merged_rect[0]) y = min(rect_cv[1], merged_rect[1]) w = max(rect_cv[0] + rect_cv[2], merged_rect[0] + merged_rect[2]) - x h = (rect_cv[1] + rect_cv[3]) - merged_rect[1] merged_rectangles[i] = (x, y, w, h) merge_attempted = True break if not merge_attempted: merged_rectangles.append(rect_cv) else: merged_rectangles.append(rect_cv) merged_rect_info_list = [{'x': r[0], 'y': r[1], 'width': r[2], 'height': r[3]} for r in merged_rectangles] return merged_rect_info_list def hsv_image(img=None, img_path=None,lower_range = np.array([10, 200, 200]),upper_range=np.array([20,255,255])): if img is not None: image = img else: if img_path == None: raise Exception("img與img_path引數不能都為None,請傳入引數") # 讀取影像 image = cv2.imread(img_path) image = cv2.GaussianBlur(image, (5, 5), 3) # 將影像轉換為HSV顏色空間 hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 建立掩碼 mask = cv2.inRange(hsv_img, lower_range, upper_range) #閉運算 kernel=np.ones((3,3),dtype=np.uint8) mask= closed_image = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) output_mask = mask return output_mask, image def exponential_transform(image, gamma=1.5): try: # 歸一化影像 img_normalized = image.astype('float32') / 255.0 # 進行指數變換 img_adjusted = 255.0 * (img_normalized ** gamma) # 逆歸一化還原影像 adjusted_image = np.clip(img_adjusted, 0, 255).astype('uint8') return adjusted_image except Exception as e: print(f"處理影像時出現錯誤:{e}") return None def filter_byarea(contours,min_area_threshold=5): # 根據輪廓面積從大到小排序 contours = sorted(contours, key=cv2.contourArea, reverse=True) # 過濾掉面積小於閾值的輪廓 filtered_contours = [contour for contour in contours if cv2.contourArea(contour) >= min_area_threshold] return filtered_contours def get_contours_bymask(mask): # 尋找輪廓 contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)#cv2.CHAIN_APPROX_NONE return contours def get_rect_info(contours) -> list: # 獲取邊與高寬平行的外接矩陣點(不一定是最小外接矩形,最小外接矩形使用cv2.minAreaRect(contour)) rect_info_list = list() for contour in contours: # 獲取包圍輪廓的矩形,邊與高寬平行 x, y, w, h = cv2.boundingRect(contour) # 儲存包圍矩形資訊 rect_info_list.append({ 'x': x, # 矩形左上角點的 x 座標。 'y': y, # 矩形左上角點的 y 座標 'width': w, 'height': h }) return rect_info_list def draw_rect_by_rect_info_list(rect_info_list, image=None, img_path=None, output_folder=None,name=""): if image is not None: image = image else: if img_path == None: raise Exception("img與img_path引數不能都為None,請傳入引數") # 讀取影像 image = cv2.imread(img_path) # 建立影像副本,避免修改原始影像 result_image = image.copy() # 獲取當前時間 # 生成儲存影像的檔名,基於當前時間 current_time = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S-%ms") # 遍歷矩形資訊列表 for rect_info in rect_info_list: # 獲取矩形的座標和尺寸資訊 x, y, width, height = rect_info['x'], rect_info['y'], rect_info['width'], rect_info['height'] # 繪製矩形框,綠色 cv2.rectangle(result_image, (x, y), (x + width, y + height), (0, 255, 0), 2) # 儲存繪製後的輪廓圖 cv2.imwrite(f"./images/result_rect_list2_{current_time}.png", result_image) return result_image def filter_rect_info_list_by_wh(rect_info_list,min_w_thresh=5,min__h_thresh=5): result=[] for rect_info in rect_info_list: width=rect_info['width'] height=rect_info['height'] if width= rect2['x'] and rect1['y'] >= rect2['y'] and rect1['x'] + rect1['width'] <= rect2['x'] + rect2['width'] and rect1['y'] + rect1['height'] <= rect2['y'] + rect2['height']): is_inner = True break # 如果沒有其他矩形完全包含當前矩形,則將其加入到過濾後的列表中 if not is_inner: filtered_rectangles.append(rect1) return filtered_rectangles def returnmask(img=None, img_path=None, imshowFlag=False,lower_range=None,upper_range=None): # offset=0微調偏移量,暫時不用 if img is not None: image = img else: if img_path == None: raise Exception("img與img_path引數不能都為None,請傳入引數") # 讀取影像 image = cv2.imread(img_path) # 將影像轉換為HSV顏色空間 hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 建立掩碼 mask = cv2.inRange(hsv_img, lower_range, upper_range) output_mask=mask return output_mask, image def calculate_center(rect): ###找到中心點 x_center = rect['x'] + rect['width'] / 2 y_center = rect['y'] + rect['height'] / 2 return (x_center, y_center) def distance_between_centers(center1, center2): ####計算兩個中心點的距離 return math.sqrt((center1[0] - center2[0])**2 + (center1[1] - center2[1])**2) #把相鄰或者相交的框整合成一個大框 def merge_close_rectangles(rect_info_list, max_distance_threshold): merged_rectangles = [] merged = False for rect1 in rect_info_list: rect1_center = calculate_center(rect1) for i, rect2 in enumerate(merged_rectangles): rect2_center = calculate_center(rect2) distance = distance_between_centers(rect1_center, rect2_center) # print("舊框之間的距離:distance",distance) if distance < max_distance_threshold: # Merge rectangles new_x = min(rect1['x'], rect2['x']) new_y = min(rect1['y'], rect2['y']) new_width = max(rect1['x'] + rect1['width'], rect2['x'] + rect2['width']) - new_x new_height = max(rect1['y'] + rect1['height'], rect2['y'] + rect2['height']) - new_y merged_rectangles[i] = {'x': new_x, 'y': new_y, 'width': new_width, 'height': new_height} merged = True break if not merged: merged_rectangles.append(rect1) merged = False return merged_rectangles #透過判定框框離影像邊緣距離的遠近,來看這個框是否保留 def filter_rectangles_by_edge(rectangles, image_width, image_height, edge_threshold): filtered_rectangles = [] for rect in rectangles: x_center = rect['x'] + rect['width'] / 2 y_center = rect['y'] + rect['height'] / 2 # Calculate distances to image edges distance_to_left = x_center distance_to_right = image_width - x_center distance_to_top = y_center distance_to_bottom = image_height - y_center # Check if any distance is less than the threshold if (distance_to_left > edge_threshold and distance_to_right > edge_threshold and distance_to_top > edge_threshold and distance_to_bottom > edge_threshold): filtered_rectangles.append(rect) return filtered_rectangles def find_largest_rectangle(rect_info_list, min_area_threshold): if not rect_info_list: return None # 初始化最大矩形的面積和索引 max_area = -1 largest_rect = None # 遍歷矩形資訊列表,找到面積最大且大於等於最小面積閾值的矩形 for rect in rect_info_list: x = rect['x'] y = rect['y'] width = rect['width'] height = rect['height'] area = width * height if area >= min_area_threshold and area > max_area and height > width: max_area = area largest_rect = rect return largest_rect def return_result(image=None, img_path=None, direction=None): if image is not None: image = image else: if img_path == None: raise Exception("img與img_path引數不能都為None,請傳入引數") # 讀取影像 image = cv2.imread(img_path) # 獲取影像的寬度和高度 pic_height, pic_width = image.shape[:2] lower_range=np.array([6, 0, 51]) upper_range=np.array([173, 88, 124]) output_mask, image=returnmask(img=image, img_path=None, imshowFlag=False, lower_range=lower_range, upper_range=upper_range) # 返回一個二值圖和一個原始影像 contours = get_contours_bymask(output_mask) contours = filter_byarea(contours, min_area_threshold=2) # 過濾掉兩個畫素點之間的小影像,這一步是為了過濾誤檢的小白點 rect_info_list = get_rect_info(contours) # 定義最小面積閾值 min_area_threshold = 1500 # 呼叫函式找到面積最大且大於等於最小面積閾值的矩形 largest_rectangle = find_largest_rectangle(rect_info_list, min_area_threshold) if largest_rectangle is not None: # print("面積最大且大於等於最小面積閾值的矩形資訊:", largest_rectangle) x = largest_rectangle['x'] y = largest_rectangle['y'] width = largest_rectangle['width'] height = largest_rectangle['height'] if direction ==1: if 30 < y < 110 and height >= 80: # print("樣品沒有問題") result = True else: # print("樣品有問題") result = False if direction ==2: if pic_height-110 < y+height < pic_height -30 and height >= 80: # print("樣品沒有問題") result = True else: # print("樣品有問題") result = False else: # print("輸入列表為空或沒有符合條件的矩形。") # print("樣品有問題") result = False return result def classify_lingjian(rect_info_list, image_orginal, image, direction=None): #####此時傳入的圖片應該都是高大於寬的型別 if image is not None: image = image else: raise Exception("img引數為None,請傳入引數") # 獲取列表中元素的數量 num_elements = len(rect_info_list) # 獲取影像尺寸 pic_height, pic_width = image.shape[:2] pic_ratio = pic_height / pic_width maxarea = 0 minarea = 1000000 maxheight = 0 minheight = 1000000 # 根據元素數量執行相關操作 if num_elements == 1: # print("樣品僅僅只有一個框") result = False elif num_elements == 2: # print("樣品僅僅只有兩個框") for rect_info in rect_info_list: rect = rect_info area1 = rect['width'] * rect['height'] maxarea = max(area1, maxarea) minarea = min(area1, minarea) ratio = maxarea/minarea #暫且先用面積比來進行第一次分類 如果大於3則認為是第一天發的產品,如果小於3,則認為是第二天發的第一個產品 if ratio >= 3 : result1 = return_result(image=image_orginal, img_path=None, direction=direction) else: result1 = False result = result1 elif num_elements == 3: for rect_info in rect_info_list: rect = rect_info area1 = rect['width'] * rect['height'] height = rect['height'] maxarea = max(area1, maxarea) minarea = min(area1, minarea) maxheight = max(height,maxheight) minheight = min(height,minheight) ratio = maxarea / minarea H_ratio = maxheight / minheight if pic_ratio > 2.14: ##如果圖片寬高比過,大認為僅僅只有三個框就是認為是第二天發的1類圖片 if ratio > 1.5 and H_ratio > 2: result2 = return_result(image=image_orginal, img_path=None, direction=direction) else: result2 = False else: result2 = False result =result2 elif num_elements >= 4: # print("樣品框框個數大於等於4個框") if pic_ratio > 2.14: ##如果圖片寬高比過,大認為僅僅只有三個框就是認為是第二天發的1類圖片 # print("樣品執行return_result") result3 = return_result(image=image_orginal, img_path=None, direction=direction) else: # print("樣品區分不出是哪一類") result3 = False result =result3 else: # print("樣品沒有框框") result = False return result def finanl_result(image=None, img_path=None, direction = 0): if image is not None: image = image # 獲取影像尺寸 height, width = image.shape[:2] # 比較影像的高度和寬度 if height < width: # 需要順時針旋轉90度 image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE) else: if img_path == None: raise Exception("img與img_path引數不能都為None,請傳入引數") # 讀取影像 image = cv2.imread(img_path) lower_range = np.array([19, 50, 50]) upper_range = np.array([23, 125, 125]) lower_range1 = np.array([13, 18, 70]) upper_range1 = np.array([24, 89, 164]) mask1 = cv2.inRange(image,lower_range1,upper_range1) # # lower_range2 = np.array([11, 6, 56]) upper_range2 = np.array([26, 80, 153]) mask2 = cv2.inRange(image, lower_range2, upper_range2) # mask3 = cv2.bitwise_or(mask1,mask2) image_orignal = image.copy() image = exponential_transform(image, gamma=1.125) output_mask, image = hsv_image(img=image, img_path=None, lower_range=lower_range, upper_range=upper_range) output_mask = cv2.bitwise_or(output_mask,mask3) contours = get_contours_bymask(output_mask) contours = filter_byarea(contours, min_area_threshold=20) rect_info_list1 = get_rect_info(contours) ### 過濾掉極端的框 rect_info_list2 = filter_rect_info_list_by_wh(rect_info_list1, min_w_thresh=10, min__h_thresh=10) ### 呼叫中心點距離合並函式 max_distance_threshold = 35 # Example threshold for maximum distance rect_info_list = merge_close_rectangles(rect_info_list2, max_distance_threshold) #### 透過距離閾值把幾個重合的框進行合併· # 呼叫臨邊合併函式 merged_rect_info_list1 = merge_close_rectangles_by_linbian(rect_info_list) # 呼叫相交合並函式 merged_rect_info_list = merge_intersecting_rectangles(merged_rect_info_list1) # ## 過濾掉包含的框 rect_info_list3 = filter_inner_rectangles(merged_rect_info_list) ## 利用面積和寬高比過濾掉邊邊角角 # 設定過濾條件 min_area_threshold = 180 # 過濾掉面積小於600的矩形 width_to_height_ratio_thresh = 3.5 # 過濾掉寬度是高度的3倍以上的矩形 filtered_rectangles_1 = filter_rectangles(rect_info_list3, min_area_threshold, width_to_height_ratio_thresh) ### 透過框和圖片邊緣的距離來判斷這個框是否是干擾框,結果返回正常框 filtered_rectangles = filter_rectangles_by_edge(filtered_rectangles_1, image.shape[1], image.shape[0],edge_threshold=25) # draw_rect_by_rect_info_list(filtered_rectangles, image=image, img_path=None, output_folder="./", name="") result = classify_lingjian(filtered_rectangles, image_orignal, image, direction) # print("result", result) return result if __name__ == '__main__': image_path = "./liangpin/jump_cap1_5_1_20240729132711_OK_.png" image = cv2.imread(image_path,1) start = time.time() direction = 1 # 1代表帽子在左邊 2代表帽子在右邊 result = finanl_result(image, direction=direction) end = time.time() print("result", result) # folder_path = r'./loujian' # 沒有誤報 # # 呼叫函式遍歷圖片資料夾 # iamge_path_list = traverse_images(folder_path) # start = time.time() # get_result(iamge_path_list) # end = time.time()`

相關文章