`
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<min_w_thresh and height<min__h_thresh:
continue
else:
result.append(rect_info)
return result
def filter_inner_rectangles(rect_info_list): ###合并包围框
filtered_rectangles = []
# 遍历每一个矩形
for rect1 in rect_info_list:
is_inner = False
# 检查是否有其他矩形完全包含当前矩形
for rect2 in rect_info_list:
if rect1 != rect2:
if (rect1['x'] >= 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()`
标签:info,image,有色,height,给定,merged,图像,rectangles,rect
From: https://www.cnblogs.com/littlecute555/p/18356362