Files
jigsaw/angle3.py
2024-12-03 11:34:51 +13:00

180 lines
6.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import cv2
import numpy as np
import itertools
import os
import time
import math
# 设置输入输出文件夹路径
input_folder = "output10" # 输入文件夹
output_folder = "output12" # 输出文件夹
# 创建输出文件夹(如果不存在)
os.makedirs(output_folder, exist_ok=True)
start_time = time.time()
import numpy as np
def order_points(points, clockwise=True):
"""
将四边形的四个点按顺时针或逆时针顺序排列。
:param points: 四边形的四个点 [(x1, y1), (x2, y2), (x3, y3), (x4, y4)]
:param clockwise: 是否按顺时针排列,默认 True顺时针False逆时针
:return: 排序后的点
"""
# 计算质心
center = np.mean(points, axis=0)
# 计算每个点相对于质心的角度
angles = [np.arctan2(point[1] - center[1], point[0] - center[0]) for point in points]
# 按角度排序,顺时针(从大到小)或逆时针(从小到大)
sorted_points = [point for _, point in sorted(zip(angles, points), reverse=clockwise)]
return sorted_points
def counter_order(contours):
for contour in contours:
# 计算轮廓的签名面积
area = cv2.contourArea(contour, oriented=True)
# 如果面积是负值,说明轮廓是逆时针
return area > 0
def calculate_weight(points, non_transparent_area):
contour = np.array(points, dtype=np.int32)
area = cv2.contourArea(contour)
if area / non_transparent_area < 0.8:
return -1
edges = [np.linalg.norm(np.array(points[i]) - np.array(points[(i + 1) % 4])) for i in range(4)]
max_edge, min_edge = max(edges), min(edges)
if max_edge - min_edge > max_edge / 7:
return -1
edge_similarity = 1 - (max_edge - min_edge) / max_edge
angles = []
for i in range(4):
v1 = np.array(points[(i + 1) % 4]) - np.array(points[i])
v2 = np.array(points[(i + 3) % 4]) - np.array(points[i])
cosine_angle = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
angle = np.degrees(np.arccos(np.clip(cosine_angle, -1.0, 1.0)))
angles.append(angle)
if any(angle < 80 or angle > 100 for angle in angles):
return -1
angle_similarity = 1 - sum(abs(angle - 90) for angle in angles) / 360
square_similarity = edge_similarity * angle_similarity
return square_similarity * 0.4 + (area / non_transparent_area) * 0.6
def find_best_quadrilateral(corner_points, non_transparent_area):
quadrilaterals = list(itertools.combinations(corner_points, 4))
best_quad = None
max_weight = -1
for quad in quadrilaterals:
quad = order_points(quad)
weight = calculate_weight(quad, non_transparent_area)
if weight > max_weight:
max_weight = weight
best_quad = quad
return best_quad, max_weight
def nearest_distance(x1, y1, x2, y2):
distance = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
return distance < 2
def split_contours_into_segments(contour, quad):
segments = [[],[],[],[]]
segment_index = 0
flag = [True,True,True,True]
offset = []
for point in contour:
point = point[0]
if flag[0] & nearest_distance(point[0], point[1], quad[0][0], quad[0][1]):
segment_index += 1
flag[0] = False
if flag[1] & nearest_distance(point[0], point[1], quad[1][0], quad[1][1]):
segment_index += 1
flag[1] = False
if flag[2] & nearest_distance(point[0], point[1], quad[2][0], quad[2][1]):
segment_index += 1
flag[2] = False
if flag[3] & nearest_distance(point[0], point[1], quad[3][0], quad[3][1]):
segment_index += 1
flag[3] = False
if segment_index >= 4:
offset.append(point)
else:
segments[segment_index].append(point)
segments[0] = offset + segments[0]
return segments
def process_image(image_path, save_path):
image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
if image is None:
print(f"无法加载图片: {image_path}")
return
alpha_channel = image[:, :, 3]
non_transparent_area = np.count_nonzero(alpha_channel > 0)
if non_transparent_area == 0:
print(f"图片完全透明: {image_path}")
return
max_corners = 14
corners = cv2.goodFeaturesToTrack(alpha_channel, maxCorners=max_corners, qualityLevel=0.01, minDistance=15, blockSize=5)
if corners is not None:
corners = np.intp(corners)
corner_points = [tuple(corner.ravel()) for corner in corners]
best_quad, max_weight = find_best_quadrilateral(corner_points, non_transparent_area)
if best_quad is None:
print(f"未找到有效四边形: {image_path}")
return
output_image = cv2.cvtColor(image[:, :, :3], cv2.COLOR_BGR2RGB)
for point in corner_points:
cv2.circle(output_image, point, radius=3, color=(255, 0, 0), thickness=-1)
#cv2.polylines(output_image, [np.array(best_quad, dtype=np.int32)], isClosed=True, color=(0, 255, 0), thickness=2)
contours, _ = cv2.findContours(alpha_channel, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for contour in contours:
clockwise = counter_order(contours)
best_quad = order_points(best_quad, clockwise)
segments = split_contours_into_segments(contour, best_quad)
colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0)]
for i, segment in enumerate(segments):
if segment:
cv2.polylines(output_image, [np.array(segment)], isClosed=False, color=colors[i], thickness=2)
cv2.imwrite(save_path, cv2.cvtColor(output_image, cv2.COLOR_RGB2BGR))
else:
print(f"未检测到角点: {image_path}")
for filename in os.listdir(input_folder):
if filename.lower().endswith((".png", ".jpg", ".jpeg")):
input_path = os.path.join(input_folder, filename)
output_path = os.path.join(output_folder, filename)
process_image(input_path, output_path)
end_time = time.time()
print(f"处理图片的时间: {end_time - start_time:.4f}")
print(f"所有图片已处理完成,结果保存在: {output_folder}")