Initial commit
133
01split.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 读取输入图像
|
||||||
|
image = cv2.imread('input.jpg')
|
||||||
|
|
||||||
|
# 检查图像是否读取成功
|
||||||
|
if image is None:
|
||||||
|
print("无法读取 'input.jpg',请确保文件存在且路径正确。")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# 获取图像的尺寸
|
||||||
|
height, width = image.shape[:2]
|
||||||
|
|
||||||
|
# 创建九个掩码,初始化为全黑
|
||||||
|
masks = [np.zeros((height, width), dtype=np.uint8) for _ in range(9)]
|
||||||
|
|
||||||
|
# 定义曲线参数,增加弯曲程度
|
||||||
|
amplitude_h = height / 15 # 横向曲线的最大幅度
|
||||||
|
amplitude_v = width / 15 # 纵向曲线的最大幅度
|
||||||
|
|
||||||
|
# 定义 x 和 y 坐标
|
||||||
|
x = np.arange(width)
|
||||||
|
y = np.arange(height)
|
||||||
|
|
||||||
|
# 定义横向曲线(沿着 x 轴变化),在每个区域的中间进行一次弯曲
|
||||||
|
y_h1 = np.zeros_like(x, dtype=float)
|
||||||
|
y_h2 = np.zeros_like(x, dtype=float)
|
||||||
|
|
||||||
|
# 定义横向曲线的三个段
|
||||||
|
segment_width = width / 3
|
||||||
|
|
||||||
|
for i in range(3):
|
||||||
|
start_x = i * segment_width
|
||||||
|
end_x = (i + 1) * segment_width
|
||||||
|
center_x = (start_x + end_x) / 2
|
||||||
|
|
||||||
|
mask = (x >= start_x) & (x < end_x)
|
||||||
|
x_relative = x[mask] - center_x
|
||||||
|
amplitude_modifier = np.cos(np.pi * x_relative / (end_x - start_x)) ** 2
|
||||||
|
|
||||||
|
y_h1[mask] = (height / 3) + amplitude_h * amplitude_modifier * np.sin(np.pi * x_relative / (end_x - start_x))
|
||||||
|
y_h2[mask] = (2 * height / 3) + amplitude_h * amplitude_modifier * np.sin(np.pi * x_relative / (end_x - start_x))
|
||||||
|
|
||||||
|
# 确保 y 值在图像范围内
|
||||||
|
y_h1 = np.clip(y_h1, 0, height - 1).astype(int)
|
||||||
|
y_h2 = np.clip(y_h2, 0, height - 1).astype(int)
|
||||||
|
|
||||||
|
# 定义纵向曲线的三个段
|
||||||
|
segment_height = height / 3
|
||||||
|
|
||||||
|
x_v1 = np.zeros_like(y, dtype=float)
|
||||||
|
x_v2 = np.zeros_like(y, dtype=float)
|
||||||
|
|
||||||
|
for i in range(3):
|
||||||
|
start_y = i * segment_height
|
||||||
|
end_y = (i + 1) * segment_height
|
||||||
|
center_y = (start_y + end_y) / 2
|
||||||
|
|
||||||
|
mask = (y >= start_y) & (y < end_y)
|
||||||
|
y_relative = y[mask] - center_y
|
||||||
|
amplitude_modifier = np.cos(np.pi * y_relative / (end_y - start_y)) ** 2
|
||||||
|
|
||||||
|
x_v1[mask] = (width / 3) + amplitude_v * amplitude_modifier * np.sin(np.pi * y_relative / (end_y - start_y))
|
||||||
|
x_v2[mask] = (2 * width / 3) + amplitude_v * amplitude_modifier * np.sin(np.pi * y_relative / (end_y - start_y))
|
||||||
|
|
||||||
|
# 确保 x 值在图像范围内
|
||||||
|
x_v1 = np.clip(x_v1, 0, width - 1).astype(int)
|
||||||
|
x_v2 = np.clip(x_v2, 0, width - 1).astype(int)
|
||||||
|
|
||||||
|
# 创建网格
|
||||||
|
Y, X = np.meshgrid(np.arange(height), np.arange(width), indexing='ij')
|
||||||
|
|
||||||
|
# 获取对应的曲线值
|
||||||
|
y_curve1 = y_h1[X]
|
||||||
|
y_curve2 = y_h2[X]
|
||||||
|
x_curve1 = x_v1[Y]
|
||||||
|
x_curve2 = x_v2[Y]
|
||||||
|
|
||||||
|
# 计算区域索引
|
||||||
|
region = np.zeros((height, width), dtype=int)
|
||||||
|
|
||||||
|
# 定义区域编号如下:
|
||||||
|
# 0 | 1 | 2
|
||||||
|
# 3 | 4 | 5
|
||||||
|
# 6 | 7 | 8
|
||||||
|
|
||||||
|
# 上区域
|
||||||
|
upper = Y <= y_curve1
|
||||||
|
middle = (Y > y_curve1) & (Y <= y_curve2)
|
||||||
|
lower = Y > y_curve2
|
||||||
|
|
||||||
|
# 左、中、右区域
|
||||||
|
left = X <= x_curve1
|
||||||
|
center = (X > x_curve1) & (X <= x_curve2)
|
||||||
|
right = X > x_curve2
|
||||||
|
|
||||||
|
# 分配区域
|
||||||
|
region[upper & left] = 0
|
||||||
|
region[upper & center] = 1
|
||||||
|
region[upper & right] = 2
|
||||||
|
region[middle & left] = 3
|
||||||
|
region[middle & center] = 4
|
||||||
|
region[middle & right] = 5
|
||||||
|
region[lower & left] = 6
|
||||||
|
region[lower & center] = 7
|
||||||
|
region[lower & right] = 8
|
||||||
|
|
||||||
|
# 创建掩码
|
||||||
|
for idx in range(9):
|
||||||
|
masks[idx][region == idx] = 255
|
||||||
|
|
||||||
|
# 确保输出目录存在
|
||||||
|
output_dir = 'output'
|
||||||
|
if not os.path.exists(output_dir):
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
# 应用掩码并保存带透明度的图片
|
||||||
|
for idx, mask in enumerate(masks):
|
||||||
|
# 提取 RGB 部分
|
||||||
|
img_part = cv2.bitwise_and(image, image, mask=mask)
|
||||||
|
|
||||||
|
# 转换为 BGRA
|
||||||
|
img_part = cv2.cvtColor(img_part, cv2.COLOR_BGR2BGRA)
|
||||||
|
|
||||||
|
# 设置 alpha 通道
|
||||||
|
img_part[:, :, 3] = mask
|
||||||
|
|
||||||
|
# 保存为 PNG 格式以保留透明度
|
||||||
|
cv2.imwrite(os.path.join(output_dir, f'output{idx + 1}.png'), img_part)
|
||||||
|
|
||||||
|
print("图片已成功切割成9张,并保存到 'output' 目录下,带有透明背景。")
|
||||||
27
02rotate.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import os
|
||||||
|
import random
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
def rotate_images_random_angle(input_folder, output_folder):
|
||||||
|
if not os.path.exists(output_folder):
|
||||||
|
os.makedirs(output_folder)
|
||||||
|
|
||||||
|
for filename in os.listdir(input_folder):
|
||||||
|
if filename.lower().endswith('.png'):
|
||||||
|
input_path = os.path.join(input_folder, filename)
|
||||||
|
output_path = os.path.join(output_folder, filename)
|
||||||
|
angle = random.uniform(0, 360)
|
||||||
|
try:
|
||||||
|
with Image.open(input_path) as img:
|
||||||
|
rotated_img = img.rotate(angle, expand=True)
|
||||||
|
rotated_img.save(output_path)
|
||||||
|
print(f"Rotated {filename} by {angle:.2f} degrees.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error processing {filename}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
input_folder = 'output'
|
||||||
|
output_folder = 'output2'
|
||||||
|
rotate_images_random_angle(input_folder, output_folder)
|
||||||
50
03combine.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import os
|
||||||
|
from math import ceil, sqrt
|
||||||
|
from PIL import Image
|
||||||
|
import random
|
||||||
|
|
||||||
|
# 设置图片文件夹路径
|
||||||
|
folder_path = 'output2'
|
||||||
|
output_folder = 'output3'
|
||||||
|
output_path = 'output3/merged_image.png'
|
||||||
|
|
||||||
|
if not os.path.exists(output_folder):
|
||||||
|
os.makedirs(output_folder)
|
||||||
|
|
||||||
|
# 获取所有 PNG 文件
|
||||||
|
images = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if f.lower().endswith('.png')]
|
||||||
|
if not images:
|
||||||
|
print("没有找到 PNG 图片。")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
random.shuffle(images)
|
||||||
|
|
||||||
|
num_images = len(images)
|
||||||
|
# 计算行数和列数,使得接近正方形
|
||||||
|
cols = ceil(sqrt(num_images))
|
||||||
|
rows = ceil(num_images / cols)
|
||||||
|
|
||||||
|
# 打开所有图片并获取尺寸
|
||||||
|
opened_images = [Image.open(img) for img in images]
|
||||||
|
widths, heights = zip(*(img.size for img in opened_images))
|
||||||
|
|
||||||
|
# 假设所有图片尺寸相同,如果不同,可以选择调整大小
|
||||||
|
max_width = max(widths)
|
||||||
|
max_height = max(heights)
|
||||||
|
|
||||||
|
# 创建一个新的空白图片
|
||||||
|
merged_width = cols * max_width
|
||||||
|
merged_height = rows * max_height
|
||||||
|
merged_image = Image.new('RGBA', (merged_width, merged_height), (255, 255, 255, 0))
|
||||||
|
|
||||||
|
# 将每张图片粘贴到合适的位置
|
||||||
|
for index, img in enumerate(opened_images):
|
||||||
|
row = index // cols
|
||||||
|
col = index % cols
|
||||||
|
x = col * max_width
|
||||||
|
y = row * max_height
|
||||||
|
merged_image.paste(img, (x, y))
|
||||||
|
|
||||||
|
# 保存合并后的图片
|
||||||
|
merged_image.save(output_path)
|
||||||
|
print(f"图片已成功合并并保存为 {output_path}")
|
||||||
84
03rectangle_dir.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import os
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def process_png(file_path, output_folder):
|
||||||
|
image = cv2.imread(file_path, cv2.IMREAD_UNCHANGED)
|
||||||
|
if image is None:
|
||||||
|
print(f"无法读取图像:{file_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if image.shape[2] < 4:
|
||||||
|
print(f"图像没有 alpha 通道:{file_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
alpha = image[:, :, 3]
|
||||||
|
_, thresh = cv2.threshold(alpha, 0, 255, cv2.THRESH_BINARY)
|
||||||
|
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
|
|
||||||
|
if not contours:
|
||||||
|
print(f"图像中没有非透明部分:{file_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
cnt = max(contours, key=cv2.contourArea)
|
||||||
|
rect = cv2.minAreaRect(cnt)
|
||||||
|
box = cv2.boxPoints(rect)
|
||||||
|
box = np.int32(box)
|
||||||
|
|
||||||
|
# 提取最小矩形内的图像并旋转
|
||||||
|
width = int(rect[1][0])
|
||||||
|
height = int(rect[1][1])
|
||||||
|
|
||||||
|
if width == 0 or height == 0:
|
||||||
|
print(f"最小矩形的宽度或高度为零:{file_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
src_pts = box.astype("float32")
|
||||||
|
# 定义目标点为水平矩形
|
||||||
|
dst_pts = np.array([
|
||||||
|
[0, height - 1],
|
||||||
|
[0, 0],
|
||||||
|
[width - 1, 0],
|
||||||
|
[width - 1, height - 1]
|
||||||
|
], dtype="float32")
|
||||||
|
|
||||||
|
# 计算透视变换矩阵
|
||||||
|
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
|
||||||
|
warped = cv2.warpPerspective(image, M, (width, height))
|
||||||
|
|
||||||
|
base, ext = os.path.splitext(os.path.basename(file_path))
|
||||||
|
extracted_filename = f"{base}_extracted{ext}"
|
||||||
|
extracted_path = os.path.join(output_folder, extracted_filename)
|
||||||
|
|
||||||
|
# 保存带 alpha 通道的图像
|
||||||
|
if warped.shape[2] == 4:
|
||||||
|
cv2.imwrite(extracted_path, warped)
|
||||||
|
else:
|
||||||
|
# 如果没有 alpha 通道,转换为 RGB
|
||||||
|
cv2.imwrite(extracted_path, cv2.cvtColor(warped, cv2.COLOR_BGR2RGB))
|
||||||
|
|
||||||
|
print(f"提取并旋转后的图像已保存为:{extracted_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
input_folder = 'output'
|
||||||
|
output_folder = 'output2'
|
||||||
|
|
||||||
|
if not os.path.exists(output_folder):
|
||||||
|
os.makedirs(output_folder)
|
||||||
|
|
||||||
|
png_files = [f for f in os.listdir(input_folder) if f.lower().endswith('.png')]
|
||||||
|
|
||||||
|
if not png_files:
|
||||||
|
print("在指定的文件夹中未找到 PNG 文件。")
|
||||||
|
return
|
||||||
|
|
||||||
|
for filename in png_files:
|
||||||
|
file_path = os.path.join(input_folder, filename)
|
||||||
|
print(f"正在处理文件:{file_path}")
|
||||||
|
process_png(file_path, output_folder)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
79
03rectangle_file.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import os
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def process_merged_image(file_path, output_folder):
|
||||||
|
image = cv2.imread(file_path, cv2.IMREAD_UNCHANGED)
|
||||||
|
if image is None:
|
||||||
|
print(f"无法读取图像:{file_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if image.shape[2] < 4:
|
||||||
|
print(f"图像没有 alpha 通道:{file_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
alpha = image[:, :, 3]
|
||||||
|
_, thresh = cv2.threshold(alpha, 0, 255, cv2.THRESH_BINARY)
|
||||||
|
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
|
|
||||||
|
if not contours:
|
||||||
|
print("图像中没有非透明部分。")
|
||||||
|
return
|
||||||
|
|
||||||
|
for idx, cnt in enumerate(contours, start=1):
|
||||||
|
rect = cv2.minAreaRect(cnt)
|
||||||
|
box = cv2.boxPoints(rect)
|
||||||
|
box = np.int32(box)
|
||||||
|
|
||||||
|
width = int(rect[1][0])
|
||||||
|
height = int(rect[1][1])
|
||||||
|
|
||||||
|
if width == 0 or height == 0:
|
||||||
|
print(f"第 {idx} 个最小矩形的宽度或高度为零,跳过。")
|
||||||
|
continue
|
||||||
|
|
||||||
|
src_pts = box.astype("float32")
|
||||||
|
# 定义目标点为水平矩形
|
||||||
|
dst_pts = np.array([
|
||||||
|
[0, height - 1],
|
||||||
|
[0, 0],
|
||||||
|
[width - 1, 0],
|
||||||
|
[width - 1, height - 1]
|
||||||
|
], dtype="float32")
|
||||||
|
|
||||||
|
# 计算透视变换矩阵
|
||||||
|
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
|
||||||
|
warped = cv2.warpPerspective(image, M, (width, height))
|
||||||
|
|
||||||
|
base, ext = os.path.splitext(os.path.basename(file_path))
|
||||||
|
extracted_filename = f"{base}_extracted_{idx}{ext}"
|
||||||
|
extracted_path = os.path.join(output_folder, extracted_filename)
|
||||||
|
|
||||||
|
# 保存带 alpha 通道的图像
|
||||||
|
if warped.shape[2] == 4:
|
||||||
|
cv2.imwrite(extracted_path, warped)
|
||||||
|
else:
|
||||||
|
# 如果没有 alpha 通道,转换为 RGB
|
||||||
|
cv2.imwrite(extracted_path, cv2.cvtColor(warped, cv2.COLOR_BGR2RGB))
|
||||||
|
|
||||||
|
print(f"提取并旋转后的图像已保存为:{extracted_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
input_image = os.path.join('output3', 'merged_image.png')
|
||||||
|
output_folder = 'output4'
|
||||||
|
|
||||||
|
if not os.path.exists(output_folder):
|
||||||
|
os.makedirs(output_folder)
|
||||||
|
|
||||||
|
if not os.path.isfile(input_image):
|
||||||
|
print(f"输入图像不存在:{input_image}")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"正在处理图像:{input_image}")
|
||||||
|
process_merged_image(input_image, output_folder)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
196
04extract_borders.py
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
import os
|
||||||
|
from PIL import Image
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def get_edge_opaque_pixels_with_limit(image_path):
|
||||||
|
"""
|
||||||
|
提取图片中每一行和每一列最左、最右、最上、最下的完全不透明像素(alpha == 255),
|
||||||
|
并根据位置限制过滤。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_path (str): 图片文件路径。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 包含四个边的像素信息列表。
|
||||||
|
"""
|
||||||
|
with Image.open(image_path).convert("RGBA") as img:
|
||||||
|
width, height = img.size
|
||||||
|
pixels = img.load()
|
||||||
|
limit_left = width / 3
|
||||||
|
limit_right = 2 * width / 3
|
||||||
|
limit_top = height / 3
|
||||||
|
limit_bottom = 2 * height / 3
|
||||||
|
|
||||||
|
left_pixels = []
|
||||||
|
right_pixels = []
|
||||||
|
top_pixels = []
|
||||||
|
bottom_pixels = []
|
||||||
|
|
||||||
|
# 左边
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
r, g, b, a = pixels[x, y]
|
||||||
|
if a == 255 and x <= limit_left:
|
||||||
|
left_pixels.append({
|
||||||
|
"position": (x, y),
|
||||||
|
"color": (r, g, b, a)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
|
# 右边
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width - 1, -1, -1):
|
||||||
|
r, g, b, a = pixels[x, y]
|
||||||
|
if a == 255 and x >= limit_right:
|
||||||
|
right_pixels.append({
|
||||||
|
"position": (x, y),
|
||||||
|
"color": (r, g, b, a)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
|
# 上边
|
||||||
|
for x in range(width):
|
||||||
|
for y in range(height):
|
||||||
|
r, g, b, a = pixels[x, y]
|
||||||
|
if a == 255 and y <= limit_top:
|
||||||
|
top_pixels.append({
|
||||||
|
"position": (x, y),
|
||||||
|
"color": (r, g, b, a)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
|
# 下边
|
||||||
|
for x in range(width):
|
||||||
|
for y in range(height - 1, -1, -1):
|
||||||
|
r, g, b, a = pixels[x, y]
|
||||||
|
if a == 255 and y >= limit_bottom:
|
||||||
|
bottom_pixels.append({
|
||||||
|
"position": (x, y),
|
||||||
|
"color": (r, g, b, a)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
|
||||||
|
return {
|
||||||
|
"left": left_pixels,
|
||||||
|
"right": right_pixels,
|
||||||
|
"top": top_pixels,
|
||||||
|
"bottom": bottom_pixels
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_edge_opaque_pixels_with_limit_numpy(image_path):
|
||||||
|
"""
|
||||||
|
使用NumPy提取图片中每一行和每一列最左、最右、最上、最下的完全不透明像素(alpha == 255),
|
||||||
|
并根据位置限制过滤。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_path (str): 图片文件路径。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 包含四个边的像素信息列表。
|
||||||
|
"""
|
||||||
|
with Image.open(image_path).convert("RGBA") as img:
|
||||||
|
data = np.array(img)
|
||||||
|
height, width, _ = data.shape
|
||||||
|
limit_left = width / 3
|
||||||
|
limit_right = 2 * width / 3
|
||||||
|
limit_top = height / 3
|
||||||
|
limit_bottom = 2 * height / 3
|
||||||
|
|
||||||
|
left_pixels = []
|
||||||
|
right_pixels = []
|
||||||
|
top_pixels = []
|
||||||
|
bottom_pixels = []
|
||||||
|
|
||||||
|
# 左边
|
||||||
|
for y in range(height):
|
||||||
|
row = data[y, :, :]
|
||||||
|
opaque_indices = np.where(row[:, 3] == 255)[0]
|
||||||
|
valid_indices = opaque_indices[opaque_indices <= limit_left]
|
||||||
|
if valid_indices.size > 0:
|
||||||
|
x = valid_indices[0]
|
||||||
|
r, g, b, a = row[x]
|
||||||
|
left_pixels.append({
|
||||||
|
"position": (int(x), y),
|
||||||
|
"color": (int(r), int(g), int(b), int(a))
|
||||||
|
})
|
||||||
|
|
||||||
|
# 右边
|
||||||
|
for y in range(height):
|
||||||
|
row = data[y, :, :]
|
||||||
|
opaque_indices = np.where(row[:, 3] == 255)[0]
|
||||||
|
valid_indices = opaque_indices[opaque_indices >= limit_right]
|
||||||
|
if valid_indices.size > 0:
|
||||||
|
x = valid_indices[-1] # 最右边的第一个满足条件的像素
|
||||||
|
r, g, b, a = row[x]
|
||||||
|
right_pixels.append({
|
||||||
|
"position": (int(x), y),
|
||||||
|
"color": (int(r), int(g), int(b), int(a))
|
||||||
|
})
|
||||||
|
|
||||||
|
# 上边
|
||||||
|
for x in range(width):
|
||||||
|
column = data[:, x, :]
|
||||||
|
opaque_indices = np.where(column[:, 3] == 255)[0]
|
||||||
|
valid_indices = opaque_indices[opaque_indices <= limit_top]
|
||||||
|
if valid_indices.size > 0:
|
||||||
|
y = valid_indices[0]
|
||||||
|
r, g, b, a = column[y]
|
||||||
|
top_pixels.append({
|
||||||
|
"position": (x, int(y)),
|
||||||
|
"color": (int(r), int(g), int(b), int(a))
|
||||||
|
})
|
||||||
|
|
||||||
|
# 下边
|
||||||
|
for x in range(width):
|
||||||
|
column = data[:, x, :]
|
||||||
|
opaque_indices = np.where(column[:, 3] == 255)[0]
|
||||||
|
valid_indices = opaque_indices[opaque_indices >= limit_bottom]
|
||||||
|
if valid_indices.size > 0:
|
||||||
|
y = valid_indices[-1] # 最下边的第一个满足条件的像素
|
||||||
|
r, g, b, a = column[y]
|
||||||
|
bottom_pixels.append({
|
||||||
|
"position": (x, int(y)),
|
||||||
|
"color": (int(r), int(g), int(b), int(a))
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"left": left_pixels,
|
||||||
|
"right": right_pixels,
|
||||||
|
"top": top_pixels,
|
||||||
|
"bottom": bottom_pixels
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def process_directory(directory_path, use_numpy=False):
|
||||||
|
"""
|
||||||
|
遍历指定文件夹下所有图片文件,提取每张图片的边缘不透明像素信息。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
directory_path (str): 文件夹路径。
|
||||||
|
use_numpy (bool): 是否使用NumPy加速处理。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 每个文件对应的四个方向的像素信息。
|
||||||
|
"""
|
||||||
|
supported_extensions = ('.png', '.jpg', '.jpeg', '.bmp', '.gif', '.tiff')
|
||||||
|
result_map = {}
|
||||||
|
|
||||||
|
for filename in os.listdir(directory_path):
|
||||||
|
if filename.lower().endswith(supported_extensions):
|
||||||
|
file_path = os.path.join(directory_path, filename)
|
||||||
|
if use_numpy:
|
||||||
|
edge_pixels = get_edge_opaque_pixels_with_limit_numpy(file_path)
|
||||||
|
else:
|
||||||
|
edge_pixels = get_edge_opaque_pixels_with_limit(file_path)
|
||||||
|
result_map[filename] = edge_pixels
|
||||||
|
|
||||||
|
return result_map
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
directory_path = "output4" # 替换为你的文件夹路径
|
||||||
|
use_numpy = True # 设置为True使用NumPy优化,False使用PIL
|
||||||
|
result = process_directory(directory_path, use_numpy)
|
||||||
|
|
||||||
196
04extract_borders_slim.py
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
import os
|
||||||
|
from PIL import Image
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def get_edge_opaque_pixels_with_limit_filtered_colors(image_path):
|
||||||
|
"""
|
||||||
|
提取图片中每一行和每一列最左、最右、最上、最下的完全不透明像素(alpha == 255),
|
||||||
|
并根据位置限制过滤,去掉位置和alpha通道。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_path (str): 图片文件路径。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 包含四个边的颜色信息列表。
|
||||||
|
"""
|
||||||
|
with Image.open(image_path).convert("RGBA") as img:
|
||||||
|
width, height = img.size
|
||||||
|
pixels = img.load()
|
||||||
|
limit_left = width / 3
|
||||||
|
limit_right = 2 * width / 3
|
||||||
|
limit_top = height / 3
|
||||||
|
limit_bottom = 2 * height / 3
|
||||||
|
|
||||||
|
left_colors = []
|
||||||
|
right_colors = []
|
||||||
|
top_colors = []
|
||||||
|
bottom_colors = []
|
||||||
|
|
||||||
|
# 左边
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
r, g, b, a = pixels[x, y]
|
||||||
|
if a == 255 and x <= limit_left:
|
||||||
|
left_colors.append((r, g, b))
|
||||||
|
break
|
||||||
|
|
||||||
|
# 右边
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width - 1, -1, -1):
|
||||||
|
r, g, b, a = pixels[x, y]
|
||||||
|
if a == 255 and x >= limit_right:
|
||||||
|
right_colors.append((r, g, b))
|
||||||
|
break
|
||||||
|
|
||||||
|
# 上边
|
||||||
|
for x in range(width):
|
||||||
|
for y in range(height):
|
||||||
|
r, g, b, a = pixels[x, y]
|
||||||
|
if a == 255 and y <= limit_top:
|
||||||
|
top_colors.append((r, g, b))
|
||||||
|
break
|
||||||
|
|
||||||
|
# 下边
|
||||||
|
for x in range(width):
|
||||||
|
for y in range(height - 1, -1, -1):
|
||||||
|
r, g, b, a = pixels[x, y]
|
||||||
|
if a == 255 and y >= limit_bottom:
|
||||||
|
bottom_colors.append((r, g, b))
|
||||||
|
break
|
||||||
|
|
||||||
|
return {
|
||||||
|
"left": left_colors,
|
||||||
|
"right": right_colors,
|
||||||
|
"top": top_colors,
|
||||||
|
"bottom": bottom_colors
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_edge_opaque_pixels_with_limit_filtered_colors_numpy(image_path):
|
||||||
|
"""
|
||||||
|
使用NumPy提取图片中每一行和每一列最左、最右、最上、最下的完全不透明像素(alpha == 255),
|
||||||
|
并根据位置限制过滤,去掉位置和alpha通道。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_path (str): 图片文件路径。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 包含四个边的颜色信息列表。
|
||||||
|
"""
|
||||||
|
with Image.open(image_path).convert("RGBA") as img:
|
||||||
|
data = np.array(img)
|
||||||
|
height, width, _ = data.shape
|
||||||
|
limit_left = width / 3
|
||||||
|
limit_right = 2 * width / 3
|
||||||
|
limit_top = height / 3
|
||||||
|
limit_bottom = 2 * height / 3
|
||||||
|
|
||||||
|
left_colors = []
|
||||||
|
right_colors = []
|
||||||
|
top_colors = []
|
||||||
|
bottom_colors = []
|
||||||
|
|
||||||
|
# 左边
|
||||||
|
for y in range(height):
|
||||||
|
row = data[y, :, :]
|
||||||
|
opaque_indices = np.where(row[:, 3] == 255)[0]
|
||||||
|
valid_indices = opaque_indices[opaque_indices <= limit_left]
|
||||||
|
if valid_indices.size > 0:
|
||||||
|
x = valid_indices[0]
|
||||||
|
r, g, b, _ = row[x]
|
||||||
|
left_colors.append((int(r), int(g), int(b)))
|
||||||
|
|
||||||
|
# 右边
|
||||||
|
for y in range(height):
|
||||||
|
row = data[y, :, :]
|
||||||
|
opaque_indices = np.where(row[:, 3] == 255)[0]
|
||||||
|
valid_indices = opaque_indices[opaque_indices >= limit_right]
|
||||||
|
if valid_indices.size > 0:
|
||||||
|
x = valid_indices[-1]
|
||||||
|
r, g, b, _ = row[x]
|
||||||
|
right_colors.append((int(r), int(g), int(b)))
|
||||||
|
|
||||||
|
# 上边
|
||||||
|
for x in range(width):
|
||||||
|
column = data[:, x, :]
|
||||||
|
opaque_indices = np.where(column[:, 3] == 255)[0]
|
||||||
|
valid_indices = opaque_indices[opaque_indices <= limit_top]
|
||||||
|
if valid_indices.size > 0:
|
||||||
|
y = valid_indices[0]
|
||||||
|
r, g, b, _ = column[y]
|
||||||
|
top_colors.append((int(r), int(g), int(b)))
|
||||||
|
|
||||||
|
# 下边
|
||||||
|
for x in range(width):
|
||||||
|
column = data[:, x, :]
|
||||||
|
opaque_indices = np.where(column[:, 3] == 255)[0]
|
||||||
|
valid_indices = opaque_indices[opaque_indices >= limit_bottom]
|
||||||
|
if valid_indices.size > 0:
|
||||||
|
y = valid_indices[-1]
|
||||||
|
r, g, b, _ = column[y]
|
||||||
|
bottom_colors.append((int(r), int(g), int(b)))
|
||||||
|
|
||||||
|
return {
|
||||||
|
"left": left_colors,
|
||||||
|
"right": right_colors,
|
||||||
|
"top": top_colors,
|
||||||
|
"bottom": bottom_colors
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def process_directory_filtered(directory_path, use_numpy=False):
|
||||||
|
"""
|
||||||
|
遍历指定文件夹下所有图片文件,提取每张图片的边缘不透明像素颜色信息。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
directory_path (str): 文件夹路径。
|
||||||
|
use_numpy (bool): 是否使用NumPy加速处理。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 每个文件对应的四个方向的颜色信息列表。
|
||||||
|
"""
|
||||||
|
supported_extensions = ('.png', '.jpg', '.jpeg', '.bmp', '.gif', '.tiff')
|
||||||
|
result_map = {}
|
||||||
|
|
||||||
|
for filename in os.listdir(directory_path):
|
||||||
|
if filename.lower().endswith(supported_extensions):
|
||||||
|
file_path = os.path.join(directory_path, filename)
|
||||||
|
if use_numpy:
|
||||||
|
edge_colors = get_edge_opaque_pixels_with_limit_filtered_colors_numpy(file_path)
|
||||||
|
else:
|
||||||
|
edge_colors = get_edge_opaque_pixels_with_limit_filtered_colors(file_path)
|
||||||
|
result_map[filename] = edge_colors
|
||||||
|
|
||||||
|
return result_map
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
directory_path = "output4" # 替换为你的文件夹路径
|
||||||
|
use_numpy = True # 设置为True使用NumPy优化,False使用PIL
|
||||||
|
result = process_directory_filtered(directory_path, use_numpy)
|
||||||
|
|
||||||
|
for filename, edges in result.items():
|
||||||
|
print(f"文件: {filename}")
|
||||||
|
|
||||||
|
# 左边
|
||||||
|
print(" 左边的最左不透明像素颜色:")
|
||||||
|
for color in edges["left"]:
|
||||||
|
print(f" 颜色: {color}")
|
||||||
|
|
||||||
|
# 右边
|
||||||
|
print(" 右边的最右不透明像素颜色:")
|
||||||
|
for color in edges["right"]:
|
||||||
|
print(f" 颜色: {color}")
|
||||||
|
|
||||||
|
# 上边
|
||||||
|
print(" 上边的最上不透明像素颜色:")
|
||||||
|
for color in edges["top"]:
|
||||||
|
print(f" 颜色: {color}")
|
||||||
|
|
||||||
|
# 下边
|
||||||
|
print(" 下边的最下不透明像素颜色:")
|
||||||
|
for color in edges["bottom"]:
|
||||||
|
print(f" 颜色: {color}")
|
||||||
|
|
||||||
|
print("\n")
|
||||||
162
angle.py
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import itertools
|
||||||
|
import os
|
||||||
|
import time # 引入 time 模块
|
||||||
|
|
||||||
|
# 设置输入输出文件夹路径
|
||||||
|
input_folder = "output10" # 输入文件夹
|
||||||
|
output_folder = "output12" # 输出文件夹
|
||||||
|
|
||||||
|
# 创建输出文件夹(如果不存在)
|
||||||
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
def order_points(points):
|
||||||
|
"""
|
||||||
|
将四边形的四个点按顺时针顺序排列。
|
||||||
|
:param points: 四边形的四个点 [(x1, y1), (x2, y2), (x3, y3), (x4, y4)]
|
||||||
|
: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))]
|
||||||
|
|
||||||
|
return sorted_points
|
||||||
|
|
||||||
|
|
||||||
|
# 定义处理每张图片的函数
|
||||||
|
def process_image(image_path, save_path):
|
||||||
|
# 加载图片
|
||||||
|
image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
|
||||||
|
if image is None:
|
||||||
|
print(f"无法加载图片: {image_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
img_height, img_width = image.shape[:2]
|
||||||
|
|
||||||
|
alpha_channel = image[:, :, 3]
|
||||||
|
|
||||||
|
non_transparent_area = np.count_nonzero(alpha_channel > 0)
|
||||||
|
if non_transparent_area == 0: # 如果图片完全透明,跳过
|
||||||
|
print(f"图片完全透明: {image_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 双边滤波平滑处理
|
||||||
|
#smooth_mask = cv2.bilateralFilter(alpha_channel, d=9, sigmaColor=75, sigmaSpace=75)
|
||||||
|
|
||||||
|
# 高斯模糊平滑处理
|
||||||
|
#blurred_mask = cv2.GaussianBlur(smooth_mask, (3, 3), sigmaX=0, sigmaY=0)
|
||||||
|
|
||||||
|
# 二值化处理:转换为边框 mask
|
||||||
|
_, border_mask = cv2.threshold(alpha_channel, 1, 255, cv2.THRESH_BINARY)
|
||||||
|
|
||||||
|
# 找到边框上的轮廓
|
||||||
|
contours, _ = cv2.findContours(border_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
|
|
||||||
|
# 创建一个空白的边框图片
|
||||||
|
border_image = np.zeros_like(alpha_channel)
|
||||||
|
cv2.drawContours(border_image, contours, -1, color=255, thickness=1)
|
||||||
|
|
||||||
|
# 角点检测 (最多 16 个角点)
|
||||||
|
max_corners = 14
|
||||||
|
corners = cv2.goodFeaturesToTrack(border_image, 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]
|
||||||
|
|
||||||
|
quadrilaterals = list(itertools.combinations(corner_points, 4))
|
||||||
|
|
||||||
|
def calculate_weight(points):
|
||||||
|
|
||||||
|
contour = np.array(points, dtype=np.int32)
|
||||||
|
area = cv2.contourArea(contour)
|
||||||
|
|
||||||
|
if area/non_transparent_area < 0.8:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if not cv2.isContourConvex(contour):
|
||||||
|
return -1
|
||||||
|
|
||||||
|
# print(area/non_transparent_area)
|
||||||
|
|
||||||
|
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 # 使用非透明面积归一化
|
||||||
|
|
||||||
|
best_quad = None
|
||||||
|
max_weight = -1
|
||||||
|
for quad in quadrilaterals:
|
||||||
|
# 对最优四边形的点进行排序
|
||||||
|
quad = order_points(quad)
|
||||||
|
|
||||||
|
weight = calculate_weight(quad)
|
||||||
|
if weight > max_weight:
|
||||||
|
max_weight = weight
|
||||||
|
best_quad = quad
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if best_quad is None:
|
||||||
|
print(f"未找到有效四边形: {image_path}")
|
||||||
|
|
||||||
|
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.drawContours(output_image, contours, -1, color=(0, 0, 255), thickness=1) # 蓝色 (BGR: 255, 0, 0)
|
||||||
|
|
||||||
|
cv2.imwrite(save_path, cv2.cvtColor(output_image, cv2.COLOR_RGB2BGR))
|
||||||
|
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
area = cv2.contourArea(np.array(best_quad, dtype=np.int32))
|
||||||
|
print(image_path+"\t\t"+str(area / non_transparent_area))
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 用蓝色标记边框
|
||||||
|
cv2.drawContours(output_image, contours, -1, color=(0, 0, 255), thickness=1) # 蓝色 (BGR: 255, 0, 0)
|
||||||
|
|
||||||
|
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}")
|
||||||
182
angle2.py
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import itertools
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
# 设置输入输出文件夹路径
|
||||||
|
input_folder = "output10" # 输入文件夹
|
||||||
|
output_folder = "output12" # 输出文件夹
|
||||||
|
|
||||||
|
# 创建输出文件夹(如果不存在)
|
||||||
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
def order_points(points):
|
||||||
|
"""
|
||||||
|
将四边形的四个点按顺时针顺序排列。
|
||||||
|
:param points: 四边形的四个点 [(x1, y1), (x2, y2), (x3, y3), (x4, y4)]
|
||||||
|
: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))]
|
||||||
|
return sorted_points
|
||||||
|
|
||||||
|
def order_counters(contours):
|
||||||
|
counterclockwise_contours = []
|
||||||
|
for contour in contours:
|
||||||
|
# 计算轮廓的签名面积
|
||||||
|
area = cv2.contourArea(contour, oriented=True)
|
||||||
|
|
||||||
|
# 如果面积是负值,说明轮廓是逆时针
|
||||||
|
if area > 0:
|
||||||
|
# 将轮廓反转为逆时针
|
||||||
|
contour = contour[::-1]
|
||||||
|
|
||||||
|
# 添加到新的列表中
|
||||||
|
counterclockwise_contours.append(contour)
|
||||||
|
return counterclockwise_contours
|
||||||
|
|
||||||
|
def calculate_weight(points, non_transparent_area):
|
||||||
|
"""
|
||||||
|
计算四边形的权重,用于选择最优四边形。
|
||||||
|
:param points: 四边形的四个点 [(x1, y1), (x2, y2), (x3, y3), (x4, y4)]
|
||||||
|
:param non_transparent_area: 非透明区域的面积
|
||||||
|
:return: 四边形的权重(-1 表示无效四边形)
|
||||||
|
"""
|
||||||
|
contour = np.array(points, dtype=np.int32)
|
||||||
|
area = cv2.contourArea(contour)
|
||||||
|
|
||||||
|
# 筛选条件 1: 四边形的面积比例
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 筛选条件 2: 边长相似性
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 筛选条件 3: 角度相似性
|
||||||
|
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.5 + (area / non_transparent_area) * 0.5 # 使用非透明面积归一化
|
||||||
|
|
||||||
|
def find_best_quadrilateral(corner_points, non_transparent_area):
|
||||||
|
"""
|
||||||
|
从角点中筛选最优四边形。
|
||||||
|
:param corner_points: 检测到的角点 [(x1, y1), (x2, y2), ...]
|
||||||
|
:param non_transparent_area: 非透明区域的面积
|
||||||
|
:return: 最优四边形的点集和最大权重
|
||||||
|
"""
|
||||||
|
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 process_image(image_path, save_path):
|
||||||
|
# 加载图片
|
||||||
|
image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
|
||||||
|
if image is None:
|
||||||
|
print(f"无法加载图片: {image_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
img_height, img_width = image.shape[:2]
|
||||||
|
alpha_channel = image[:, :, 3]
|
||||||
|
|
||||||
|
non_transparent_area = np.count_nonzero(alpha_channel > 0)
|
||||||
|
if non_transparent_area == 0: # 如果图片完全透明,跳过
|
||||||
|
print(f"图片完全透明: {image_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 找到边框上的轮廓
|
||||||
|
contours, _ = cv2.findContours(alpha_channel, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
|
|
||||||
|
# 创建一个空白的边框图片
|
||||||
|
border_image = np.zeros_like(alpha_channel)
|
||||||
|
cv2.drawContours(border_image, contours, -1, color=255, thickness=1)
|
||||||
|
|
||||||
|
# 角点检测 (最多 16 个角点)
|
||||||
|
max_corners = 14
|
||||||
|
corners = cv2.goodFeaturesToTrack(border_image, 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}")
|
||||||
|
|
||||||
|
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.drawContours(output_image, contours, -1, color=(0, 0, 255), thickness=1) # 蓝色 (BGR: 255, 0, 0)
|
||||||
|
|
||||||
|
cv2.imwrite(save_path, cv2.cvtColor(output_image, cv2.COLOR_RGB2BGR))
|
||||||
|
return
|
||||||
|
|
||||||
|
area = cv2.contourArea(np.array(best_quad, dtype=np.int32))
|
||||||
|
print(image_path+"\t\t"+str(area / non_transparent_area))
|
||||||
|
|
||||||
|
# 绘制最优四边形和角点
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 用蓝色标记边框
|
||||||
|
cv2.drawContours(output_image, contours, -1, color=(0, 0, 255), thickness=1) # 蓝色 (BGR: 255, 0, 0)
|
||||||
|
|
||||||
|
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}")
|
||||||
179
angle3.py
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
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}")
|
||||||
304
angle4.py
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import itertools
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
# 设置输入输出文件夹路径
|
||||||
|
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 extract_and_save_pixels(image , coordinates, out_image_file):
|
||||||
|
# 创建一个空白图像,大小为原图大小
|
||||||
|
transparent_img = np.zeros_like(image)
|
||||||
|
|
||||||
|
# 设置指定坐标的像素值
|
||||||
|
for x, y in coordinates:
|
||||||
|
if 0 <= x < image.shape[1] and 0 <= y < image.shape[0]: # 检查是否在范围内
|
||||||
|
transparent_img[y, x] = image[y, x]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 获取开始坐标和结束坐标
|
||||||
|
start_point = coordinates[0]
|
||||||
|
end_point = coordinates[-1]
|
||||||
|
|
||||||
|
# 计算连线的中间点
|
||||||
|
middle_point = (
|
||||||
|
(start_point[0] + end_point[0]) // 2,
|
||||||
|
(start_point[1] + end_point[1]) // 2,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 检查中间点是否在图像范围内
|
||||||
|
if not (0 <= middle_point[0] < image.shape[1] and 0 <= middle_point[1] < image.shape[0]):
|
||||||
|
raise ValueError(f"中间点 {middle_point} 超出了图像边界。")
|
||||||
|
|
||||||
|
# 提取中间点的 alpha 通道值
|
||||||
|
alpha_value = image[middle_point[1], middle_point[0], 3] # alpha 通道在第 4 位
|
||||||
|
|
||||||
|
# 判断透明性
|
||||||
|
is_transparent = alpha_value == 0
|
||||||
|
print(f"中间点 {middle_point} 的透明状态: {'透明' if is_transparent else '非透明'}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 转换坐标为 NumPy 数组
|
||||||
|
coordinates_array = np.array(coordinates)
|
||||||
|
|
||||||
|
# 计算最小外接矩形
|
||||||
|
rect = cv2.minAreaRect(coordinates_array)
|
||||||
|
size = tuple(map(int, rect[1]))
|
||||||
|
|
||||||
|
# 如果宽度或高度小于5像素,终止方法
|
||||||
|
if size[0] < 5 or size[1] < 5:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 扩展矩形尺寸
|
||||||
|
expanded_width = size[0] + 4 # 每侧扩展 2 个像素
|
||||||
|
expanded_height = size[1] + 4
|
||||||
|
expanded_size = (expanded_width, expanded_height)
|
||||||
|
|
||||||
|
|
||||||
|
# 获取旋转矩阵并旋转图像
|
||||||
|
angle = rect[-1]
|
||||||
|
if angle < -45:
|
||||||
|
angle += 90
|
||||||
|
center = tuple(map(int, rect[0]))
|
||||||
|
size = tuple(map(int, rect[1]))
|
||||||
|
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
|
||||||
|
# 对图像进行仿射变换
|
||||||
|
rotated_img = cv2.warpAffine(transparent_img, rotation_matrix, (image.shape[1], image.shape[0]),
|
||||||
|
flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0, 0))
|
||||||
|
|
||||||
|
# 确保尺寸为正数
|
||||||
|
size = (max(int(size[0]), 1), max(int(size[1]), 1))
|
||||||
|
|
||||||
|
# 检查裁剪范围是否超出边界
|
||||||
|
x_start = max(int(center[0] - expanded_size[0] / 2), 0)
|
||||||
|
x_end = min(int(center[0] + expanded_size[0] / 2), rotated_img.shape[1])
|
||||||
|
y_start = max(int(center[1] - expanded_size[1] / 2), 0)
|
||||||
|
y_end = min(int(center[1] + expanded_size[1] / 2), rotated_img.shape[0])
|
||||||
|
|
||||||
|
if x_start >= x_end or y_start >= y_end:
|
||||||
|
raise ValueError("裁剪范围无效,可能是输入坐标错误或图片过小。")
|
||||||
|
|
||||||
|
# 裁剪最小矩形
|
||||||
|
cropped_img = rotated_img[y_start:y_end, x_start:x_end]
|
||||||
|
|
||||||
|
# 如果宽度小于高度,旋转 90°
|
||||||
|
if cropped_img.shape[1] < cropped_img.shape[0]:
|
||||||
|
cropped_img = cv2.rotate(cropped_img, cv2.ROTATE_90_CLOCKWISE)
|
||||||
|
|
||||||
|
# 判断是否需要旋转 180°
|
||||||
|
h, w, _ = cropped_img.shape
|
||||||
|
half_h = h // 2
|
||||||
|
|
||||||
|
upper_half = cropped_img[:half_h, :, 3] # 提取上半部分的 alpha 通道
|
||||||
|
lower_half = cropped_img[half_h:, :, 3] # 提取下半部分的 alpha 通道
|
||||||
|
|
||||||
|
upper_non_transparent = np.sum(upper_half > 0)
|
||||||
|
lower_non_transparent = np.sum(lower_half > 0)
|
||||||
|
|
||||||
|
# 如果上半部分非透明像素多于下半部分,旋转 180°
|
||||||
|
if upper_non_transparent > lower_non_transparent:
|
||||||
|
cropped_img = cv2.rotate(cropped_img, cv2.ROTATE_180)
|
||||||
|
|
||||||
|
|
||||||
|
# 检查裁剪结果是否为空
|
||||||
|
if cropped_img.size == 0:
|
||||||
|
raise ValueError("裁剪结果为空,请检查坐标范围。")
|
||||||
|
|
||||||
|
# 保存结果
|
||||||
|
cv2.imwrite("output13/"+str(is_transparent)+"_"+out_image_file, cropped_img)
|
||||||
|
print(f"结果图片已保存到 {output_path}")
|
||||||
|
|
||||||
|
|
||||||
|
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 add_suffix(file_path, suffix):
|
||||||
|
# 分割文件名和扩展名
|
||||||
|
dir_name, file_name_with_ext = os.path.split(file_path)
|
||||||
|
file_name, file_ext = os.path.splitext(file_name_with_ext)
|
||||||
|
|
||||||
|
# 添加后缀
|
||||||
|
new_file_name = f"{file_name}{suffix}{file_ext}"
|
||||||
|
|
||||||
|
return new_file_name
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
extract_and_save_pixels(image, segment, add_suffix(save_path,"_" + str(i)))
|
||||||
|
|
||||||
|
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}")
|
||||||
BIN
combined_image.png
Normal file
|
After Width: | Height: | Size: 978 KiB |
96
compare.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from skimage.metrics import structural_similarity as ssim
|
||||||
|
|
||||||
|
def load_images(folder):
|
||||||
|
"""Load all PNG images from a folder, categorized by prefix."""
|
||||||
|
true_images = {}
|
||||||
|
false_images = {}
|
||||||
|
for filename in os.listdir(folder):
|
||||||
|
if filename.endswith('.png'):
|
||||||
|
filepath = os.path.join(folder, filename)
|
||||||
|
image = cv2.imread(filepath, cv2.IMREAD_UNCHANGED) # Load image with alpha channel
|
||||||
|
if filename.startswith("True_"):
|
||||||
|
true_images[filename] = image
|
||||||
|
elif filename.startswith("False_"):
|
||||||
|
false_images[filename] = image
|
||||||
|
return true_images, false_images
|
||||||
|
|
||||||
|
def resize_image(image, size=(125, 50)):
|
||||||
|
"""Resize an image to a standard size."""
|
||||||
|
return cv2.resize(image, size, interpolation=cv2.INTER_AREA)
|
||||||
|
|
||||||
|
def calculate_ssim(image1, image2):
|
||||||
|
"""Calculate SSIM between two images."""
|
||||||
|
if image1.shape[2] == 4: # Handle alpha channel
|
||||||
|
image1 = cv2.cvtColor(image1, cv2.COLOR_BGRA2BGR)
|
||||||
|
if image2.shape[2] == 4: # Handle alpha channel
|
||||||
|
image2 = cv2.cvtColor(image2, cv2.COLOR_BGRA2BGR)
|
||||||
|
gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
|
||||||
|
gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
|
||||||
|
score, _ = ssim(gray1, gray2, full=True)
|
||||||
|
return score
|
||||||
|
|
||||||
|
def calculate_color_similarity(image1, image2):
|
||||||
|
"""Compare color histograms of two images."""
|
||||||
|
if image1.shape[2] == 4: # Handle alpha channel
|
||||||
|
image1 = cv2.cvtColor(image1, cv2.COLOR_BGRA2BGR)
|
||||||
|
if image2.shape[2] == 4: # Handle alpha channel
|
||||||
|
image2 = cv2.cvtColor(image2, cv2.COLOR_BGRA2BGR)
|
||||||
|
hist1 = cv2.calcHist([image1], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])
|
||||||
|
hist2 = cv2.calcHist([image2], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256])
|
||||||
|
hist1 = cv2.normalize(hist1, hist1).flatten()
|
||||||
|
hist2 = cv2.normalize(hist2, hist2).flatten()
|
||||||
|
return cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
|
||||||
|
|
||||||
|
def calculate_combined_similarity(image1, image2):
|
||||||
|
"""Combine SSIM and color histogram similarity."""
|
||||||
|
# ssim_score = calculate_ssim(image1, image2)
|
||||||
|
color_score = calculate_color_similarity(image1, image2)
|
||||||
|
# Weight SSIM and color similarity equally (adjust weights as needed)
|
||||||
|
return 1 * color_score
|
||||||
|
|
||||||
|
def find_most_similar_images(true_images, false_images):
|
||||||
|
"""Find the most similar image for each 'True_' image among 'False_' images."""
|
||||||
|
results = {}
|
||||||
|
for true_name, true_image in true_images.items():
|
||||||
|
max_similarity = -1
|
||||||
|
most_similar_image = None
|
||||||
|
for false_name, false_image in false_images.items():
|
||||||
|
# resized_true = resize_image(true_image)
|
||||||
|
# resized_false = resize_image(false_image)
|
||||||
|
|
||||||
|
if true_name.split("_")[2] == false_name.split("_")[2]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
similarity = calculate_combined_similarity(true_image, false_image)
|
||||||
|
if similarity > max_similarity:
|
||||||
|
max_similarity = similarity
|
||||||
|
most_similar_image = false_name
|
||||||
|
results[true_name] = (most_similar_image, max_similarity)
|
||||||
|
os.makedirs("output14\\"+true_name+"_"+most_similar_image,exist_ok=True)
|
||||||
|
shutil.copyfile('output13\\'+true_name, 'output14\\'+true_name+"_"+most_similar_image+"\\"+true_name)
|
||||||
|
shutil.copyfile('output13\\' + most_similar_image, 'output14\\' +true_name+"_"+most_similar_image+"\\" + most_similar_image)
|
||||||
|
|
||||||
|
# shutil.copyfile('output12\\'+ "segment_"+true_name.split("_")[2]+".png", 'output14\\'+true_name+"_"+most_similar_image+"\\segment_"+true_name.split("_")[2]+".png")
|
||||||
|
# shutil.copyfile('output12\\' +"segment_"+most_similar_image.split("_")[2]+".png", 'output14\\'+true_name+"_"+most_similar_image+"\\segment_"+most_similar_image.split("_")[2]+".png")
|
||||||
|
|
||||||
|
shutil.copyfile('output10\\'+ "segment_"+true_name.split("_")[2]+".png", 'output14\\'+true_name+"_"+most_similar_image+"\\segment_"+true_name.split("_")[2]+".png")
|
||||||
|
shutil.copyfile('output10\\' +"segment_"+most_similar_image.split("_")[2]+".png", 'output14\\'+true_name+"_"+most_similar_image+"\\segment_"+most_similar_image.split("_")[2]+".png")
|
||||||
|
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def main():
|
||||||
|
folder = 'output13' # Replace with your folder path
|
||||||
|
true_images, false_images = load_images(folder)
|
||||||
|
results = find_most_similar_images(true_images, false_images)
|
||||||
|
for true_image, (most_similar, similarity) in results.items():
|
||||||
|
print(f"True Image: {true_image}, Most Similar False Image: {most_similar}, Similarity: {similarity:.4f}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
92
extract_mask.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
# 读取图像
|
||||||
|
image_path = 'jigsaw_mask.png' # 替换为您的图像路径
|
||||||
|
image = cv2.imread(image_path)
|
||||||
|
|
||||||
|
if image is None:
|
||||||
|
raise ValueError("无法读取图像,请检查路径是否正确。")
|
||||||
|
|
||||||
|
# 转换为RGB
|
||||||
|
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||||
|
|
||||||
|
# 转换为灰度图
|
||||||
|
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||||
|
|
||||||
|
# 应用固定阈值进行二值化
|
||||||
|
_, mask = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
|
||||||
|
|
||||||
|
# 进行形态学操作以连接断裂的线条
|
||||||
|
# 定义较大的核
|
||||||
|
kernel = np.ones((1,1), np.uint8)
|
||||||
|
|
||||||
|
# 增加膨胀次数以连接断裂
|
||||||
|
mask = cv2.dilate(mask, kernel, iterations=1)
|
||||||
|
|
||||||
|
# 闭运算进一步连接
|
||||||
|
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=5)
|
||||||
|
|
||||||
|
# 再次确保掩膜是二值的
|
||||||
|
_, mask = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)
|
||||||
|
|
||||||
|
# 创建 Alpha 通道
|
||||||
|
alpha = cv2.bitwise_not(mask)
|
||||||
|
|
||||||
|
# 确保 Alpha 通道只有0和255
|
||||||
|
alpha = cv2.threshold(alpha, 127, 255, cv2.THRESH_BINARY)[1]
|
||||||
|
|
||||||
|
# 创建纯白色背景
|
||||||
|
white_bg = 255 * np.ones_like(image_rgb, dtype=np.uint8)
|
||||||
|
|
||||||
|
# 合并 RGB 图像和 Alpha 通道
|
||||||
|
final_rgb = cv2.bitwise_and(white_bg, white_bg, mask=cv2.bitwise_not(mask))
|
||||||
|
image_rgba = cv2.cvtColor(final_rgb, cv2.COLOR_RGB2RGBA)
|
||||||
|
image_rgba[:, :, 3] = alpha
|
||||||
|
|
||||||
|
# 将 OpenCV 的 RGBA 图像转换为 Pillow Image 对象
|
||||||
|
image_pil = Image.fromarray(image_rgba)
|
||||||
|
|
||||||
|
# 转换为 NumPy 数组以进一步处理
|
||||||
|
data = np.array(image_pil)
|
||||||
|
|
||||||
|
# 分离颜色通道
|
||||||
|
red, green, blue, alpha = data.T
|
||||||
|
|
||||||
|
# 定义黑色的阈值范围
|
||||||
|
black_threshold = 50 # 根据需要调整
|
||||||
|
|
||||||
|
# 创建掩膜:黑色区域为True,其余为False
|
||||||
|
black_areas = (red < black_threshold) & (green < black_threshold) & (blue < black_threshold)
|
||||||
|
|
||||||
|
# 设置黑色区域的 Alpha 为0,非黑色区域的 Alpha 为255
|
||||||
|
data[..., 3][black_areas.T] = 0
|
||||||
|
data[..., 3][~black_areas.T] = 255
|
||||||
|
|
||||||
|
# 将非黑色区域设置为纯白色
|
||||||
|
data[..., 0:3][~black_areas.T] = [255, 255, 255]
|
||||||
|
|
||||||
|
# 创建新的 Image 对象
|
||||||
|
final_image = Image.fromarray(data)
|
||||||
|
|
||||||
|
# 保存结果为 PNG(支持透明度)
|
||||||
|
output_path = 'mask.png'
|
||||||
|
final_image.save(output_path)
|
||||||
|
|
||||||
|
print(f"已保存带纯白背景和透明部分的图像到 {output_path}")
|
||||||
|
|
||||||
|
# 可选:显示图像
|
||||||
|
plt.figure(figsize=(12,6))
|
||||||
|
plt.subplot(1,2,1)
|
||||||
|
plt.title('原图')
|
||||||
|
plt.imshow(image_rgb)
|
||||||
|
plt.axis('off')
|
||||||
|
|
||||||
|
plt.subplot(1,2,2)
|
||||||
|
plt.title('带纯白背景和透明部分的图像')
|
||||||
|
plt.imshow(final_image)
|
||||||
|
plt.axis('off')
|
||||||
|
|
||||||
|
plt.show()
|
||||||
BIN
jigsaw_mask.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
14
mask_split.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
# 打开目标图片和线条蒙版
|
||||||
|
image = Image.open("birds.png").convert("RGBA")
|
||||||
|
mask = Image.open("mask.png").convert("L") # 灰度图
|
||||||
|
|
||||||
|
# 将蒙版应用到图片上
|
||||||
|
# mask 是透明区域,255 表示不透明,0 表示完全透明
|
||||||
|
image.putalpha(mask)
|
||||||
|
|
||||||
|
# 保存结果
|
||||||
|
image.save("result.png")
|
||||||
115
mask_split_files.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
import scipy.ndimage as ndimage
|
||||||
|
from scipy.ndimage import label, find_objects
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
def create_segment_mask(alpha_channel):
|
||||||
|
"""
|
||||||
|
创建分割掩码,找出被透明线条分隔的区域
|
||||||
|
"""
|
||||||
|
binary_mask = alpha_channel < 255 # 透明区域
|
||||||
|
labeled_array, num_features = label(~binary_mask)
|
||||||
|
|
||||||
|
segment_masks = []
|
||||||
|
for i in range(1, num_features + 1):
|
||||||
|
segment_mask = labeled_array == i
|
||||||
|
segment_masks.append(segment_mask)
|
||||||
|
|
||||||
|
return segment_masks
|
||||||
|
|
||||||
|
|
||||||
|
def extract_segment_with_transparent_background(image, segment_mask):
|
||||||
|
"""
|
||||||
|
提取分割区域,区域外设置为透明
|
||||||
|
"""
|
||||||
|
slices = find_objects(segment_mask.astype(int))[0]
|
||||||
|
|
||||||
|
y_slice = slice(max(0, slices[0].start - 5), min(image.shape[0], slices[0].stop + 5))
|
||||||
|
x_slice = slice(max(0, slices[1].start - 5), min(image.shape[1], slices[1].stop + 5))
|
||||||
|
|
||||||
|
segment_image = np.zeros((image.shape[0], image.shape[1], 4), dtype=np.uint8)
|
||||||
|
segment_image[y_slice, x_slice][segment_mask[y_slice, x_slice]] = image[y_slice, x_slice][
|
||||||
|
segment_mask[y_slice, x_slice]]
|
||||||
|
|
||||||
|
return segment_image
|
||||||
|
|
||||||
|
|
||||||
|
def smart_crop_and_rotate(image, border_size=15):
|
||||||
|
"""
|
||||||
|
智能裁剪和随机旋转,保留指定像素的透明边框
|
||||||
|
"""
|
||||||
|
# 将numpy数组转换为PIL图像
|
||||||
|
pil_image = Image.fromarray(image)
|
||||||
|
|
||||||
|
# 找到非透明区域的边界框
|
||||||
|
bbox = pil_image.getbbox()
|
||||||
|
|
||||||
|
if bbox:
|
||||||
|
# 扩大裁剪区域,保留指定像素的透明边框
|
||||||
|
left = max(0, bbox[0] - border_size)
|
||||||
|
upper = max(0, bbox[1] - border_size)
|
||||||
|
right = min(image.shape[1], bbox[2] + border_size)
|
||||||
|
lower = min(image.shape[0], bbox[3] + border_size)
|
||||||
|
|
||||||
|
# 裁剪
|
||||||
|
cropped_image = pil_image.crop((left, upper, right, lower))
|
||||||
|
|
||||||
|
# # 随机旋转角度(-45到45度)
|
||||||
|
rotation_angle = random.uniform(-180, 180)
|
||||||
|
rotated_image = cropped_image.rotate(rotation_angle,
|
||||||
|
expand=True,
|
||||||
|
fillcolor=(0, 0, 0, 0))
|
||||||
|
|
||||||
|
return np.array(rotated_image)
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
def main(input_path, output_folder):
|
||||||
|
"""
|
||||||
|
主处理函数
|
||||||
|
"""
|
||||||
|
# 创建输出文件夹
|
||||||
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
|
|
||||||
|
# 随机数种子(保证每次运行结果可复现)
|
||||||
|
random.seed()
|
||||||
|
|
||||||
|
# 打开图像
|
||||||
|
image = Image.open(input_path)
|
||||||
|
|
||||||
|
# 转换为RGBA模式
|
||||||
|
image = image.convert("RGBA")
|
||||||
|
|
||||||
|
# 转换为numpy数组
|
||||||
|
img_array = np.array(image)
|
||||||
|
|
||||||
|
# 获取透明通道
|
||||||
|
alpha_channel = img_array[:, :, 3]
|
||||||
|
|
||||||
|
# 创建分割掩码
|
||||||
|
segment_masks = create_segment_mask(alpha_channel)
|
||||||
|
|
||||||
|
# 保存每个分割区域
|
||||||
|
for i, segment_mask in enumerate(segment_masks):
|
||||||
|
# 提取分割区域,区域外设置为透明
|
||||||
|
segment_image_array = extract_segment_with_transparent_background(img_array, segment_mask)
|
||||||
|
|
||||||
|
# 裁剪并随机旋转,保留20像素透明边框
|
||||||
|
processed_segment = smart_crop_and_rotate(segment_image_array, border_size=20)
|
||||||
|
|
||||||
|
# 从numpy数组创建图像
|
||||||
|
segment_image = Image.fromarray(processed_segment)
|
||||||
|
|
||||||
|
# 保存图像
|
||||||
|
output_path = os.path.join(output_folder, f'segment_{i + 1}.png')
|
||||||
|
segment_image.save(output_path)
|
||||||
|
|
||||||
|
print(f"分割、裁剪和旋转完成,共生成 {len(segment_masks)} 个图像片段")
|
||||||
|
|
||||||
|
|
||||||
|
# 执行分割
|
||||||
|
main('result.png', 'output10')
|
||||||
56
minRectangle.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import os
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# 输入和输出文件夹
|
||||||
|
input_folder = 'output10'
|
||||||
|
output_folder = 'output11'
|
||||||
|
|
||||||
|
# 确保输出文件夹存在
|
||||||
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
|
|
||||||
|
# 遍历输入文件夹中的所有图片
|
||||||
|
for filename in os.listdir(input_folder):
|
||||||
|
filepath = os.path.join(input_folder, filename)
|
||||||
|
|
||||||
|
# 检查是否是图片文件
|
||||||
|
if not filename.lower().endswith(('.png', '.jpg', '.jpeg')):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 读取图片
|
||||||
|
image = cv2.imread(filepath, cv2.IMREAD_UNCHANGED)
|
||||||
|
|
||||||
|
if image is None:
|
||||||
|
print(f"无法读取文件: {filepath}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 检查是否有 alpha 通道
|
||||||
|
if image.shape[2] == 4:
|
||||||
|
alpha_channel = image[:, :, 3]
|
||||||
|
_, binary_mask = cv2.threshold(alpha_channel, 1, 255, cv2.THRESH_BINARY)
|
||||||
|
else:
|
||||||
|
# 如果没有 alpha 通道,直接将非黑色区域作为非透明区域
|
||||||
|
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||||
|
_, binary_mask = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY)
|
||||||
|
|
||||||
|
# 找到非透明区域的轮廓
|
||||||
|
contours, _ = cv2.findContours(binary_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
|
|
||||||
|
if contours:
|
||||||
|
# 获取最大轮廓的最小旋转矩形
|
||||||
|
largest_contour = max(contours, key=cv2.contourArea)
|
||||||
|
rect = cv2.minAreaRect(largest_contour)
|
||||||
|
box = cv2.boxPoints(rect)
|
||||||
|
box = np.intp(box) # 修正数据类型
|
||||||
|
|
||||||
|
# 在原图上绘制矩形
|
||||||
|
result_image = image.copy()
|
||||||
|
if image.shape[2] == 4: # 如果有 alpha 通道
|
||||||
|
result_image = cv2.cvtColor(result_image, cv2.COLOR_BGRA2BGR)
|
||||||
|
cv2.drawContours(result_image, [box], 0, (0, 255, 0), 2)
|
||||||
|
|
||||||
|
# 保存结果图片
|
||||||
|
output_path = os.path.join(output_folder, filename)
|
||||||
|
cv2.imwrite(output_path, result_image)
|
||||||
|
|
||||||
|
print("处理完成,结果已保存到 output11 文件夹中。")
|
||||||
49
minRectangle2.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import os
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
|
def draw_centered_red_box(image, percentage=0.8, color=(255, 0, 0)):
|
||||||
|
"""
|
||||||
|
在图片上绘制一个红色框,框住指定比例的面积。
|
||||||
|
image: PIL Image 对象
|
||||||
|
percentage: 正方形框的面积占图片面积的比例(0-1之间)
|
||||||
|
color: 框的颜色 (R, G, B)
|
||||||
|
"""
|
||||||
|
w, h = image.size
|
||||||
|
# 计算正方形的边长
|
||||||
|
box_size = int((w * h * percentage) ** 0.5)
|
||||||
|
half_box = box_size // 2
|
||||||
|
|
||||||
|
# 确定框的中心点
|
||||||
|
center_x, center_y = w // 2, h // 2
|
||||||
|
|
||||||
|
# 计算正方形的边界
|
||||||
|
left = max(center_x - half_box, 0)
|
||||||
|
top = max(center_y - half_box, 0)
|
||||||
|
right = min(center_x + half_box, w)
|
||||||
|
bottom = min(center_y + half_box, h)
|
||||||
|
|
||||||
|
# 在图片上绘制红色框
|
||||||
|
draw = ImageDraw.Draw(image)
|
||||||
|
draw.rectangle([left, top, right, bottom], outline=color, width=3) # 边框宽度为 3 像素
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
# 文件夹路径
|
||||||
|
input_folder = "output10"
|
||||||
|
output_folder = "output10_with_red_box"
|
||||||
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
|
|
||||||
|
# 遍历文件夹中的图片
|
||||||
|
for filename in os.listdir(input_folder):
|
||||||
|
if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
|
||||||
|
file_path = os.path.join(input_folder, filename)
|
||||||
|
img = Image.open(file_path)
|
||||||
|
|
||||||
|
# 添加红色框
|
||||||
|
img_with_box = draw_centered_red_box(img, percentage=0.8)
|
||||||
|
|
||||||
|
# 保存结果
|
||||||
|
output_path = os.path.join(output_folder, filename)
|
||||||
|
img_with_box.save(output_path)
|
||||||
|
|
||||||
|
print("处理完成,已保存带框的图片到 'output10_with_red_box' 文件夹。")
|
||||||
BIN
output.png
Normal file
|
After Width: | Height: | Size: 976 KiB |
BIN
output/output1.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
output/output2.png
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
output/output3.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
output/output4.png
Normal file
|
After Width: | Height: | Size: 178 KiB |
BIN
output/output5.png
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
output/output6.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
output/output7.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
output/output8.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
output/output9.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
output10/segment_001.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
output10/segment_002.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
output10/segment_003.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
output10/segment_004.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
output10/segment_005.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
output10/segment_006.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
output10/segment_007.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
output10/segment_008.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
output10/segment_009.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
output10/segment_010.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
output10/segment_011.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
output10/segment_012.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
output10/segment_013.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
output10/segment_014.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
output10/segment_015.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
output10/segment_016.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
output10/segment_017.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
output10/segment_018.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
output10/segment_019.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
output10/segment_020.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
output10/segment_021.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
output10/segment_022.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
output10/segment_023.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
output10/segment_024.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
output10/segment_025.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
output10/segment_026.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
output10/segment_027.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
output10/segment_028.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
output10/segment_029.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
output10/segment_030.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
output10/segment_031.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
output10/segment_032.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
output10/segment_033.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
output10/segment_034.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
output10/segment_035.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
output10/segment_036.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
output10/segment_037.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
output10/segment_038.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
output10/segment_039.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
output10/segment_040.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
output10/segment_041.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
output10/segment_042.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
output10/segment_043.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
output10/segment_044.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
output10/segment_045.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
output10/segment_046.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
output10/segment_047.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
output10/segment_048.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
output10/segment_049.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
output10/segment_050.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
output10/segment_051.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
output10/segment_052.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
output10/segment_053.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
output10/segment_054.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
output11/segment_1.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
output11/segment_10.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
output11/segment_11.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
output11/segment_12.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
output11/segment_13.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
output11/segment_14.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
output11/segment_15.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
output11/segment_16.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
output11/segment_17.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
output11/segment_18.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
output11/segment_19.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
output11/segment_2.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
output11/segment_20.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
output11/segment_21.png
Normal file
|
After Width: | Height: | Size: 20 KiB |