Since the positioning block area on the answer sheet is a non-answering zone, there are relatively few interfering factors, and the positioning block detection belongs to the search of black rectangular blocks with clear targets. Therefore, the target area can be found by following the idea of searching for the maximum filled area within the sliding window.
Expand the specific area coordinates provided by the template as the window for target retrieval, search for the maximum filled area within the form, and then find the accurate target area through edge indentation. Count the filling ratio of the target area. If it meets the filling threshold, it is considered that the positioning block has been found.
Slide to search for the target area in the target window. The process is shown as follows:
The failed search results are displayed as follows(error threshold value for display):
Finally, all experiments are concluded:
Sorce code:
import cv2
import os
import numpy as np
import sys
global_extendsize = 40
# 读取目标图像和模板图像
def show_image(img):
cv2.namedWindow("1",cv2.WINDOW_FREERATIO)
cv2.imshow("1",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def detect_archor_block(gray, temp_archor):
#模板定位点的位置和大小
height, width = gray.shape[:2]
ar_lt_pt = (temp_archor[0],temp_archor[1])
ar_width = temp_archor[2]
ar_height = temp_archor[3]
ar_rb_pt = (ar_lt_pt[0] + ar_width, ar_lt_pt[1] + ar_height)
# cv2.rectangle(gray,ar_lt_pt ,ar_rb_pt, (0,0,255),2)
#向各个方向外扩75个像素
expand_range_lt = (ar_lt_pt[0] - global_extendsize if ar_lt_pt[0] - global_extendsize > 0 else 0, ar_lt_pt[1] - global_extendsize if ar_lt_pt[1] - global_extendsize > 0 else 0)
expand_range_rb = (ar_rb_pt[0] + global_extendsize if ar_rb_pt[0] + global_extendsize < width else width , ar_rb_pt[1] + global_extendsize if ar_rb_pt[1] + global_extendsize < height else height)
# cv2.rectangle(gray,expand_range_lt ,expand_range_rb, (0,0,255),2)
aim_range_image = gray[expand_range_lt[1]:expand_range_rb[1], expand_range_lt[0]: expand_range_rb[0]]
display_aim_img = cv2.cvtColor(aim_range_image, cv2.COLOR_GRAY2BGR)
#show_image(aim_range_image)
aim_range_height= aim_range_image.shape[0]
aim_range_width = aim_range_image.shape[1]
max_pixelSum = sys.maxsize
target_h_start = 0
for col in range(0,aim_range_width - ar_width):
aim_h_img = aim_range_image[ : ,col: col + ar_width]
sum_pixel = np.sum(aim_h_img)
if sum_pixel < max_pixelSum:
max_pixelSum = sum_pixel
target_h_start = col
#show_image(aim_h_img)
cv2.rectangle(display_aim_img,(target_h_start,0) ,(target_h_start + ar_width, aim_range_height), (0,255,255),2)
max_pixelSum = sys.maxsize
target_v_start = 0
for row in range(0, aim_range_height - ar_height):
aim_v_img = aim_range_image[row : row + ar_height,target_h_start: target_h_start + ar_width]
sum_pixel = np.sum(aim_v_img)
if sum_pixel < max_pixelSum:
max_pixelSum = sum_pixel
target_v_start = row
cv2.rectangle(display_aim_img,(target_h_start,target_v_start) ,(target_h_start + ar_width, target_v_start + ar_height), (0,165,255),1)
target_range_img = aim_range_image[target_v_start : target_v_start + ar_height, target_h_start: target_h_start + ar_width]
gray_average = np.sum(target_range_img) / target_range_img.size
l_scaled_level = 0
for i in range(0,2):
target_range_height = target_range_img.shape[0]
target_range_width = target_range_img.shape[1]
l_scaled_img = target_range_img[0: target_range_height, 1: target_range_width]
l_scaled_img_average = np.sum(l_scaled_img) / l_scaled_img.size
if(l_scaled_img_average < gray_average):
gray_average = l_scaled_img_average
target_range_img = l_scaled_img.copy()
l_scaled_level = i
t_scaled_level = 0
for i in range(0,2):
target_range_height = target_range_img.shape[0]
target_range_width = target_range_img.shape[1]
t_scaled_img = target_range_img[1: target_range_height, 0: target_range_width]
t_scaled_img_average = np.sum(t_scaled_img) / t_scaled_img.size
if(t_scaled_img_average < gray_average):
gray_average = t_scaled_img_average
target_range_img = t_scaled_img.copy()
t_scaled_level = i
r_scaled_level = 0
for i in range(0,2):
target_range_height = target_range_img.shape[0]
target_range_width = target_range_img.shape[1]
r_scaled_img = target_range_img[0: target_range_height, 0: target_range_width - 1]
r_scaled_img_average = np.sum(r_scaled_img) / r_scaled_img.size
if(r_scaled_img_average < gray_average):
gray_average = r_scaled_img_average
target_range_img = r_scaled_img.copy()
r_scaled_level = i
b_scaled_level = 0
for i in range(0,2):
target_range_height = target_range_img.shape[0]
target_range_width = target_range_img.shape[1]
b_scaled_img = target_range_img[0: target_range_height - 1, 0 : target_range_width]
b_scaled_img_average = np.sum(b_scaled_img) / b_scaled_img.size
if(b_scaled_img_average < gray_average):
gray_average = b_scaled_img_average
target_range_img = b_scaled_img.copy()
b_scaled_level = i
final_start_x = target_h_start + l_scaled_level
final_start_y = target_v_start + t_scaled_level
final_end_x = target_h_start + ar_width - r_scaled_level
finale_end_y = target_v_start + ar_height - b_scaled_level
cv2.rectangle(display_aim_img,(final_start_x,final_start_y) ,(final_end_x, finale_end_y), (255,0,0),1)
#display_bin_img = np.where(target_range_img > 56, 255, 0)
#display_bin_img = cv2.convertScaleAbs(display_bin_img)
#show_image(display_aim_img)
result_arr = np.where(target_range_img > 178 , 0, 1)
print_input_rate = np.sum(result_arr) / target_range_img.size
print('印刷填涂率为:' ,print_input_rate)
find : bool = False
if print_input_rate > 0.9:
find = True
result_rect = (expand_range_lt[0] + final_start_x, expand_range_lt[1] + final_start_y,final_end_x - final_start_x, finale_end_y - final_start_y)
else:
find = False
result_rect = (expand_range_lt[0], expand_range_lt[1], expand_range_rb[0], expand_range_rb[1])
_,binary = cv2.threshold(aim_range_image, 58,255, cv2.THRESH_BINARY)
# cv2.rectangle(display_aim_img,(result_rect[0],result_rect[1]) ,(result_rect[0] + result_rect[2],result_rect[1] + result_rect[3]), (0,255,0),2)
# show_image(display_aim_img)
return find, result_rect, binary
def main():
folder_path = './imgs4'
#模板定位点信息
temp_block_info =[(73, 126, 47, 30), (1526, 124, 47, 30),(76, 2166, 46, 31),(1530, 2160, 49, 32)]
paper_num = 0
error_paper_num = 0
# 递归遍历文件夹
for root, dirs, files in os.walk(folder_path):
for filename in files:
if filename.lower().endswith('.jpg'):
file_path = os.path.join(root, filename)
ori_img = cv2.imread(file_path, cv2.IMREAD_ANYCOLOR) # 目标图像
if len(ori_img.shape) == 3 and ori_img.shape[2] == 3:
gray_img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)
elif len(ori_img.shape) == 2:
gray_img = ori_img.copy()
else:
print('error: bad image')
return
#show_image(img)
paper_num += 1
lt_archor = temp_block_info[0]
rt_archor = temp_block_info[1]
lb_archor = temp_block_info[2]
rb_archor = temp_block_info[3]
# cv2.rectangle(ori_img,(lt_archor[0], lt_archor[1]) ,(lt_archor[0] +lt_archor[2] ,lt_archor[1] +lt_archor[3]), (0,0,255),2)
# show_image(ori_img)
#左上
print(f"检测第{paper_num}试卷")
exist_nofound = False
find, re_lt_rect ,aim_binary = detect_archor_block(gray_img, lt_archor)
if(find):
cv2.rectangle(ori_img,(re_lt_rect[0], re_lt_rect[1]) ,(re_lt_rect[0] +re_lt_rect[2] ,re_lt_rect[1] +re_lt_rect[3]), (0,0,255),2)
else:
display_binary= cv2.cvtColor(aim_binary, cv2.COLOR_GRAY2BGR)
ori_img[re_lt_rect[1]:re_lt_rect[3], re_lt_rect[0]:re_lt_rect[2]] = display_binary
exist_nofound = True
print(f"第{paper_num}试卷 左上定位点缺失")
#右上
find, re_rt_rect ,aim_binary = detect_archor_block(gray_img, rt_archor)
if(find):
cv2.rectangle(ori_img,(re_rt_rect[0], re_rt_rect[1]) ,(re_rt_rect[0] +re_rt_rect[2] ,re_rt_rect[1] +re_rt_rect[3]), (0,0,255),2)
else:
display_binary= cv2.cvtColor(aim_binary, cv2.COLOR_GRAY2BGR)
ori_img[re_rt_rect[1]:re_rt_rect[3], re_rt_rect[0]:re_rt_rect[2]] = display_binary
exist_nofound = True
print(f"第{paper_num}试卷 右上定位点缺失")
#左下
find, re_lb_rect ,aim_binary = detect_archor_block(gray_img, lb_archor)
if(find):
cv2.rectangle(ori_img,(re_lb_rect[0], re_lb_rect[1]) ,(re_lb_rect[0] +re_lb_rect[2] ,re_lb_rect[1] +re_lb_rect[3]), (0,0,255),2)
else:
display_binary= cv2.cvtColor(aim_binary, cv2.COLOR_GRAY2BGR)
ori_img[re_lb_rect[1]:re_lb_rect[3], re_lb_rect[0]:re_lb_rect[2]] = display_binary
exist_nofound = True
print(f"第{paper_num}试卷 左下定位点缺失")
#右下
find, re_rb_rect ,aim_binary = detect_archor_block(gray_img, rb_archor)
if(find):
cv2.rectangle(ori_img,(re_rb_rect[0], re_rb_rect[1]) ,(re_rb_rect[0] +re_rb_rect[2] ,re_rb_rect[1] +re_rb_rect[3]), (0,0,255),2)
else:
display_binary= cv2.cvtColor(aim_binary, cv2.COLOR_GRAY2BGR)
ori_img[re_rb_rect[1]:re_rb_rect[3], re_rb_rect[0]:re_rb_rect[2]] = display_binary
exist_nofound = True
print(f"第{paper_num}试卷 右下定位点缺失")
if exist_nofound:
error_paper_num += 1
show_image(ori_img)
print(f"已检测{paper_num}张试卷, {error_paper_num}张试卷存在定位点检测缺失, 识别率{1 - error_paper_num / paper_num}")
print()
if __name__ == '__main__':
main()
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。