pycvt 0.0.1.post1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycvt/__init__.py +3 -0
- pycvt/clolors/__init__.py +0 -0
- pycvt/clolors/colors.py +84 -0
- pycvt/paster/__init__.py +0 -0
- pycvt/paster/paste_image.py +54 -0
- pycvt/static/__init__.py +0 -0
- pycvt/utils/__init__.py +1 -0
- pycvt/utils/image_utils.py +152 -0
- pycvt/vision/__init__.py +0 -0
- pycvt/vision/plot_boxes.py +100 -0
- pycvt/vision/utils.py +25 -0
- pycvt-0.0.1.post1.dist-info/METADATA +39 -0
- pycvt-0.0.1.post1.dist-info/RECORD +15 -0
- pycvt-0.0.1.post1.dist-info/WHEEL +5 -0
- pycvt-0.0.1.post1.dist-info/top_level.txt +1 -0
pycvt/__init__.py
ADDED
|
File without changes
|
pycvt/clolors/colors.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import colorsys
|
|
2
|
+
import hashlib
|
|
3
|
+
from functools import lru_cache
|
|
4
|
+
|
|
5
|
+
_COLORS = {
|
|
6
|
+
# 绿色
|
|
7
|
+
'green': (0, 255, 0),
|
|
8
|
+
# 红色
|
|
9
|
+
'red': (0, 0, 255),
|
|
10
|
+
# 蓝色
|
|
11
|
+
'blue': (255, 0, 0),
|
|
12
|
+
# 黄色
|
|
13
|
+
'yellow': (0, 255, 255),
|
|
14
|
+
# 紫色
|
|
15
|
+
'purple': (255, 0, 255),
|
|
16
|
+
# 青色
|
|
17
|
+
'cyan': (255, 255, 0),
|
|
18
|
+
# 黑色
|
|
19
|
+
'black': (0, 0, 0),
|
|
20
|
+
# 白色
|
|
21
|
+
'white': (255, 255, 255),
|
|
22
|
+
# 灰色
|
|
23
|
+
'gray': (128, 128, 128),
|
|
24
|
+
# 深灰色
|
|
25
|
+
"GhostWhite": (248, 248, 255)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_luminance(bgr: tuple[int, int, int]) -> float:
|
|
30
|
+
# BGR to RGB
|
|
31
|
+
b, g, r = bgr
|
|
32
|
+
return 0.299 * r + 0.587 * g + 0.114 * b # ITU-R BT.601
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def ensure_contrast(text_color: tuple[int, int, int], background_color: tuple[int, int, int]) -> tuple[int, int, int]:
|
|
36
|
+
text_lum = get_luminance(text_color)
|
|
37
|
+
bg_lum = get_luminance(background_color)
|
|
38
|
+
|
|
39
|
+
contrast = abs(text_lum - bg_lum)
|
|
40
|
+
if contrast < 128: # 亮度差太小,自动调深背景
|
|
41
|
+
if text_lum > 128:
|
|
42
|
+
return (32, 32, 32) # 改成深灰色背景
|
|
43
|
+
else:
|
|
44
|
+
return (255, 255, 255) # 改成白色背景
|
|
45
|
+
return background_color
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_vibrant_color_from_key(key: str) -> tuple[int, int, int]:
|
|
49
|
+
# 使用 MD5 哈希获取稳定值
|
|
50
|
+
md5_bytes = hashlib.md5(key.encode()).digest() # 16 字节
|
|
51
|
+
hash_bytes = md5_bytes[-3:] # 从中随机选 3 个字节
|
|
52
|
+
|
|
53
|
+
# 使用前 3 字节作为 HSV 分量来源
|
|
54
|
+
h = hash_bytes[0] / 255.0 # Hue: [0, 1]
|
|
55
|
+
s = 0.8 + (hash_bytes[1] % 50) / 100.0 # Saturation: [0.8, 1.3] (上限1.0后裁剪)
|
|
56
|
+
v = 0.8 + (hash_bytes[2] % 50) / 100.0 # Value: [0.8, 1.3] (上限1.0后裁剪)
|
|
57
|
+
|
|
58
|
+
s = min(s, 1.0)
|
|
59
|
+
v = min(v, 1.0)
|
|
60
|
+
|
|
61
|
+
# 转为 RGB,再转为 BGR(OpenCV格式)
|
|
62
|
+
r, g, b = colorsys.hsv_to_rgb(h, s, v)
|
|
63
|
+
return int(b * 255), int(g * 255), int(r * 255) # OpenCV 使用 BGR
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@lru_cache(maxsize=1000)
|
|
67
|
+
def getcolor(key="red", bgr=True):
|
|
68
|
+
"""
|
|
69
|
+
Get a color by key, either as BGR tuple or hex string.
|
|
70
|
+
|
|
71
|
+
这是一个稳定的方法,相同的 key 总是返回相同的颜色。
|
|
72
|
+
:param key:
|
|
73
|
+
:param bgr:
|
|
74
|
+
:return:
|
|
75
|
+
"""
|
|
76
|
+
key = str(key)
|
|
77
|
+
|
|
78
|
+
if key in _COLORS:
|
|
79
|
+
cl_bgr = _COLORS[key]
|
|
80
|
+
else:
|
|
81
|
+
b, g, r = get_vibrant_color_from_key(key)
|
|
82
|
+
cl_bgr = (b, g, r)
|
|
83
|
+
_COLORS[key] = cl_bgr
|
|
84
|
+
return cl_bgr if bgr else f'#{r:02x}{g:02x}{b:02x}'
|
pycvt/paster/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from pycvt.utils.image_utils import convert_rgba, blend_rgba
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def paste_image(
|
|
9
|
+
bg_img,
|
|
10
|
+
fg_img,
|
|
11
|
+
position=None,
|
|
12
|
+
alpha=1.0,
|
|
13
|
+
):
|
|
14
|
+
"""
|
|
15
|
+
fg_img will be pasted onto bg_img at the specified position with the given alpha blending factor.
|
|
16
|
+
|
|
17
|
+
1. If position is None, a random position will be chosen within the bounds of the background image.
|
|
18
|
+
2. IF position + fg_img exceeds the bounds of bg_img, it will be clipped to fit within bg_img.
|
|
19
|
+
|
|
20
|
+
:param bg_img: Background image, can be a numpy array or a PIL image.
|
|
21
|
+
:param fg_img: Foreground image, can be a numpy array or a PIL image.
|
|
22
|
+
:param position: Tuple (y, x) indicating where to paste the foreground image on the background image.
|
|
23
|
+
:param alpha: Blending factor for the foreground image, where 0.0 is fully transparent and 1.0 is fully opaque.
|
|
24
|
+
:return: Tuple containing the modified background image and the bounding box of the pasted foreground image.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
bg_img = deepcopy(bg_img)
|
|
28
|
+
fg_img = deepcopy(fg_img)
|
|
29
|
+
|
|
30
|
+
bg_img = convert_rgba(bg_img)
|
|
31
|
+
fg_img = convert_rgba(fg_img)
|
|
32
|
+
|
|
33
|
+
img_h, img_w, = bg_img.shape[:2]
|
|
34
|
+
fg_h, fg_w, = fg_img.shape[:2]
|
|
35
|
+
|
|
36
|
+
alpha = np.clip(alpha, 0.0, 1.0)
|
|
37
|
+
|
|
38
|
+
if position is None:
|
|
39
|
+
position = np.random.randint(0, img_h - fg_h // 2), np.random.randint(0, img_w - fg_w // 2),
|
|
40
|
+
|
|
41
|
+
ymin, xmin = position
|
|
42
|
+
ymax, xmax = min(img_h, position[0] + fg_h), min(img_w, position[1] + fg_w)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
paste_size= (xmax - xmin, ymax - ymin)
|
|
46
|
+
|
|
47
|
+
bg_crop = bg_img[ymin:ymax, xmin:xmax]
|
|
48
|
+
fg_crop = fg_img[:paste_size[1], :paste_size[0]]
|
|
49
|
+
|
|
50
|
+
blended = blend_rgba(bg_crop, fg_crop, fgweight=alpha)
|
|
51
|
+
|
|
52
|
+
bg_img[ymin:ymax, xmin:xmax] = blended
|
|
53
|
+
|
|
54
|
+
return bg_img, (xmin, ymin, xmax, ymax)
|
pycvt/static/__init__.py
ADDED
|
File without changes
|
pycvt/utils/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import image_utils
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
|
|
3
|
+
import cv2
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def convert_rgba(img: np.ndarray) -> np.ndarray:
|
|
8
|
+
"""
|
|
9
|
+
支持灰度图、RGB图和RGBA图,统一输出 RGBA。
|
|
10
|
+
已经是 RGBA 的直接返回。
|
|
11
|
+
|
|
12
|
+
参数:
|
|
13
|
+
img (np.ndarray): 输入图像
|
|
14
|
+
|
|
15
|
+
返回:
|
|
16
|
+
np.ndarray: RGBA 图像
|
|
17
|
+
"""
|
|
18
|
+
if len(img.shape) == 2:
|
|
19
|
+
# 灰度图 (H, W)
|
|
20
|
+
img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGBA)
|
|
21
|
+
elif img.shape[2] == 3:
|
|
22
|
+
# RGB (H, W, 3)
|
|
23
|
+
img = cv2.cvtColor(img, cv2.COLOR_RGB2RGBA)
|
|
24
|
+
elif img.shape[2] == 4:
|
|
25
|
+
# 已经是 RGBA,直接返回
|
|
26
|
+
return img
|
|
27
|
+
else:
|
|
28
|
+
raise ValueError(f"只支持灰度图、RGB和RGBA图,当前通道数: {img.shape[2] if len(img.shape) > 2 else 1}")
|
|
29
|
+
|
|
30
|
+
return img
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def crop_image(img: np.ndarray, x: int, y: int, crop_w: int, crop_h: int) -> np.ndarray:
|
|
34
|
+
"""
|
|
35
|
+
从图像中裁剪指定区域,自动限制不会超出图像边界。
|
|
36
|
+
|
|
37
|
+
参数:
|
|
38
|
+
img (np.ndarray): 输入图像,shape (H, W, C) 或 (H, W)
|
|
39
|
+
x (int): 裁剪区域左上角的横坐标(列)
|
|
40
|
+
y (int): 裁剪区域左上角的纵坐标(行)
|
|
41
|
+
crop_w (int): 裁剪宽度
|
|
42
|
+
crop_h (int): 裁剪高度
|
|
43
|
+
|
|
44
|
+
返回:
|
|
45
|
+
np.ndarray: 裁剪后的图像区域
|
|
46
|
+
"""
|
|
47
|
+
img_h, img_w = img.shape[:2]
|
|
48
|
+
|
|
49
|
+
# 限制裁剪坐标和尺寸不超过图像范围
|
|
50
|
+
x_end = min(img_w, x + crop_w)
|
|
51
|
+
y_end = min(img_h, y + crop_h)
|
|
52
|
+
|
|
53
|
+
# 防止坐标越界(x, y不能小于0)
|
|
54
|
+
x = max(0, x)
|
|
55
|
+
y = max(0, y)
|
|
56
|
+
|
|
57
|
+
return img[y:y_end, x:x_end]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def blend_rgba(
|
|
61
|
+
bg_crop: np.ndarray,
|
|
62
|
+
fg_crop: np.ndarray,
|
|
63
|
+
**kwargs,
|
|
64
|
+
) -> np.ndarray:
|
|
65
|
+
"""
|
|
66
|
+
只替换 fg_crop 中 alpha > 0 的区域,直接覆盖 bg_crop 对应像素,
|
|
67
|
+
其他部分保留 bg_crop。
|
|
68
|
+
|
|
69
|
+
要求 bg_crop 和 fg_crop 形状相同,且均为 RGBA。
|
|
70
|
+
"""
|
|
71
|
+
if bg_crop.shape != fg_crop.shape or bg_crop.shape[2] != 4:
|
|
72
|
+
raise ValueError("输入图像需为形状相同的 RGBA 图像")
|
|
73
|
+
|
|
74
|
+
out = bg_crop.copy()
|
|
75
|
+
|
|
76
|
+
alpha_mask = fg_crop[:, :, 3] > 0 # bool mask,fg不透明区域
|
|
77
|
+
|
|
78
|
+
for c in range(4):
|
|
79
|
+
channel_bg = out[:, :, c]
|
|
80
|
+
channel_fg = fg_crop[:, :, c]
|
|
81
|
+
channel_bg[alpha_mask] = channel_fg[alpha_mask]
|
|
82
|
+
|
|
83
|
+
return out
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_opaque_bounding_box(rgba_img: np.ndarray, alpha_threshold: int = 0):
|
|
87
|
+
"""
|
|
88
|
+
给定 RGBA 图,返回不透明区域的外接矩形 (top, left, bottom, right)。
|
|
89
|
+
|
|
90
|
+
alpha_threshold:不透明的阈值,默认为0,表示alpha>0的像素算不透明。
|
|
91
|
+
|
|
92
|
+
如果全透明,则返回 None。
|
|
93
|
+
"""
|
|
94
|
+
if rgba_img.shape[2] != 4:
|
|
95
|
+
raise ValueError("输入图像必须是 RGBA 格式")
|
|
96
|
+
|
|
97
|
+
alpha = rgba_img[:, :, 3]
|
|
98
|
+
mask = alpha > alpha_threshold
|
|
99
|
+
|
|
100
|
+
if not np.any(mask):
|
|
101
|
+
return None # 全透明无外接矩形
|
|
102
|
+
|
|
103
|
+
coords = np.argwhere(mask) # 找出所有不透明像素的坐标,格式为 (y, x)
|
|
104
|
+
ymin, xmin = coords.min(axis=0)
|
|
105
|
+
ymax, xmax = coords.max(axis=0)
|
|
106
|
+
|
|
107
|
+
return (xmin, ymin, xmax, ymax)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def rotate_image_with_bound(image: np.ndarray, angle: float = None) -> np.ndarray:
|
|
111
|
+
"""
|
|
112
|
+
旋转图像,自动扩充尺寸以适应整个旋转后的图像。
|
|
113
|
+
RGB 背景填白,RGBA 背景填透明。
|
|
114
|
+
|
|
115
|
+
参数:
|
|
116
|
+
image: 输入图像 (H, W, 3) 或 (H, W, 4)
|
|
117
|
+
angle: 逆时针旋转角度,单位度,如果为 None,则随机选择一个角度。
|
|
118
|
+
|
|
119
|
+
返回:
|
|
120
|
+
旋转后自动扩充尺寸的图像
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
image = deepcopy(image)
|
|
124
|
+
|
|
125
|
+
if angle is None:
|
|
126
|
+
angle = np.random.randint(-180, 180)
|
|
127
|
+
|
|
128
|
+
(h, w) = image.shape[:2]
|
|
129
|
+
(cx, cy) = (w // 2, h // 2)
|
|
130
|
+
|
|
131
|
+
M = cv2.getRotationMatrix2D((cx, cy), -angle, 1.0)
|
|
132
|
+
|
|
133
|
+
cos = np.abs(M[0, 0])
|
|
134
|
+
sin = np.abs(M[0, 1])
|
|
135
|
+
|
|
136
|
+
new_w = int((h * sin) + (w * cos))
|
|
137
|
+
new_h = int((h * cos) + (w * sin))
|
|
138
|
+
|
|
139
|
+
M[0, 2] += (new_w / 2) - cx
|
|
140
|
+
M[1, 2] += (new_h / 2) - cy
|
|
141
|
+
|
|
142
|
+
# 判断通道数,设置填充颜色
|
|
143
|
+
if image.shape[2] == 3:
|
|
144
|
+
border_val = (255, 255, 255) # 白色填充
|
|
145
|
+
elif image.shape[2] == 4:
|
|
146
|
+
border_val = (0, 0, 0, 0) # 透明填充
|
|
147
|
+
else:
|
|
148
|
+
raise ValueError("只支持 RGB 或 RGBA 图像")
|
|
149
|
+
|
|
150
|
+
rotated = cv2.warpAffine(image, M, (new_w, new_h), flags=cv2.INTER_LINEAR,
|
|
151
|
+
borderMode=cv2.BORDER_CONSTANT, borderValue=border_val)
|
|
152
|
+
return rotated
|
pycvt/vision/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
|
|
3
|
+
import cv2
|
|
4
|
+
import numpy
|
|
5
|
+
import numpy as np
|
|
6
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
7
|
+
from easyfont import getfont
|
|
8
|
+
|
|
9
|
+
from pycvt.clolors.colors import getcolor, ensure_contrast
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def draw_text(
|
|
13
|
+
img,
|
|
14
|
+
text,
|
|
15
|
+
position,
|
|
16
|
+
font_path=None,
|
|
17
|
+
font_size=None,
|
|
18
|
+
text_color=None,
|
|
19
|
+
background_color=None
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
在图像上绘制文本
|
|
23
|
+
:param img: 图像对象(OpenCV格式)
|
|
24
|
+
:param text: 要绘制的文本(受字体支持的字符)
|
|
25
|
+
:param position: 文本位置,建议使用 xmax, ymax 作为位置 ,防止覆盖包围框
|
|
26
|
+
:param font_path: 字体路径
|
|
27
|
+
:param font_size: 字体大小
|
|
28
|
+
:param text_color: 文本颜色
|
|
29
|
+
:param background_color: 文本背景颜色
|
|
30
|
+
:return:
|
|
31
|
+
"""
|
|
32
|
+
# 将 OpenCV 图像转换为 PIL 图像
|
|
33
|
+
|
|
34
|
+
if font_path is None: # 若未指定字体路径,则使用默认字体
|
|
35
|
+
font_path = getfont()
|
|
36
|
+
|
|
37
|
+
text_color = text_color or getcolor(text)
|
|
38
|
+
background_color = background_color or getcolor("GhostWhite")
|
|
39
|
+
background_color = ensure_contrast(text_color, background_color) # 确保文本颜色和背景颜色有足够对比度
|
|
40
|
+
|
|
41
|
+
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
|
|
42
|
+
font = ImageFont.truetype(font_path, font_size)
|
|
43
|
+
draw = ImageDraw.Draw(img_pil)
|
|
44
|
+
|
|
45
|
+
spacing = font_size // 3
|
|
46
|
+
half_spacing = spacing // 2
|
|
47
|
+
|
|
48
|
+
text_bbox = draw.textbbox((0, 0), text, font=font, font_size=font_size, spacing=spacing)
|
|
49
|
+
text_bbox_h = text_bbox[3]
|
|
50
|
+
text_bbox_w = text_bbox[2]
|
|
51
|
+
|
|
52
|
+
bgpos = (
|
|
53
|
+
position[0] - text_bbox_w - half_spacing,
|
|
54
|
+
position[1] + spacing,
|
|
55
|
+
position[0] + spacing,
|
|
56
|
+
position[1] + text_bbox_h + spacing
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
text_pos = position[0] - text_bbox_w, position[1] + half_spacing
|
|
60
|
+
draw.rectangle(bgpos, fill=background_color)
|
|
61
|
+
draw.text(text_pos, text, font=font, fill=text_color, spacing=spacing)
|
|
62
|
+
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def draw_bounding_boxes(
|
|
66
|
+
image: numpy.ndarray,
|
|
67
|
+
boxes: list,
|
|
68
|
+
labels=None,
|
|
69
|
+
colors=None,
|
|
70
|
+
width=None,
|
|
71
|
+
font=None,
|
|
72
|
+
font_size=None
|
|
73
|
+
):
|
|
74
|
+
img_copy = deepcopy(image)
|
|
75
|
+
|
|
76
|
+
count = len(boxes)
|
|
77
|
+
w, h = img_copy.shape[1], img_copy.shape[0]
|
|
78
|
+
|
|
79
|
+
if colors is None:
|
|
80
|
+
colors = list(map(getcolor, labels)) if labels else [getcolor()] * count
|
|
81
|
+
|
|
82
|
+
line_width = width if width else max(int(0.0035 * min(w, h)), 2)
|
|
83
|
+
font_size = font_size if font_size else max(int(0.018 * min(w, h)), 2)
|
|
84
|
+
|
|
85
|
+
font = font if font else getfont()
|
|
86
|
+
for idx, box in enumerate(boxes):
|
|
87
|
+
color = colors[idx]
|
|
88
|
+
xmin, ymin, xmax, ymax = box
|
|
89
|
+
img_copy = cv2.rectangle(img_copy, (xmin, ymin), (xmax, ymax), color, line_width)
|
|
90
|
+
if labels:
|
|
91
|
+
label = labels[idx]
|
|
92
|
+
img_copy = draw_text(
|
|
93
|
+
img_copy,
|
|
94
|
+
label,
|
|
95
|
+
(xmax, ymax),
|
|
96
|
+
text_color=color[::-1], # OpenCV uses BGR, PIL uses RGB
|
|
97
|
+
font_path=font,
|
|
98
|
+
font_size=font_size
|
|
99
|
+
)
|
|
100
|
+
return img_copy
|
pycvt/vision/utils.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def iou(gtboxes, dtboxes):
|
|
5
|
+
'''numpy version of calculating IoU between two set of 2D bboxes.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
gtboxes (np.ndarray): Shape (B,4) of .., 4 present [x1,y1,x2,y2]
|
|
9
|
+
dtboxes,np.ndarray,shape:(N,4), 4 present [x1,y1,x2,y2].
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
np.ndarray: Shape (B,N) .
|
|
13
|
+
'''
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
gtboxes = gtboxes[:, np.
|
|
17
|
+
newaxis, :] #converse gtboxes:(B,4) to gtboxes:(B,1,4)
|
|
18
|
+
ixmin = np.maximum(gtboxes[:, :, 0], dtboxes[:, 0])
|
|
19
|
+
iymin = np.maximum(gtboxes[:, :, 1], dtboxes[:, 1])
|
|
20
|
+
ixmax = np.minimum(gtboxes[:, :, 2], dtboxes[:, 2])
|
|
21
|
+
iymax = np.minimum(gtboxes[:, :, 3], dtboxes[:, 3])
|
|
22
|
+
intersection = (ixmax - ixmin + 1) * (iymax - iymin + 1)
|
|
23
|
+
union = (gtboxes[:,:,2]-gtboxes[:,:,0]+1)*(gtboxes[:,:,3]-gtboxes[:,:,1]+1)\
|
|
24
|
+
+(dtboxes[:,2]-dtboxes[:,0]+1)*(dtboxes[:,3]-dtboxes[:,1]+1)-intersection
|
|
25
|
+
return intersection / union
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pycvt
|
|
3
|
+
Version: 0.0.1.post1
|
|
4
|
+
Summary: A lightweight Python toolkit for seamless image composition, color processing, and visualizing detection results.
|
|
5
|
+
Author-email: ferretsprite <ferret-sprite-goon@duck.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/xx025/pycvt
|
|
7
|
+
Project-URL: Repository, https://github.com/xx025/pycvt
|
|
8
|
+
Project-URL: Documentation, https://github.com/xx025/pycvt#readme
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: easyfont>=0.0.1.post10
|
|
12
|
+
Requires-Dist: imageio>=2.37.0
|
|
13
|
+
Requires-Dist: pillow>=11.2.1
|
|
14
|
+
Requires-Dist: torch>=2.7.1
|
|
15
|
+
Requires-Dist: torchvision>=0.22.1
|
|
16
|
+
Requires-Dist: ultralytics>=8.3.151
|
|
17
|
+
|
|
18
|
+
# pycvt
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
pip install pycvt --upgrade
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## dev
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
uv sync
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## API List
|
|
36
|
+
|
|
37
|
+
- [colors.py](pycvt/clolors/colors.py)
|
|
38
|
+
- [paste_image](pycvt/paster/paste_image.py)
|
|
39
|
+
- wait for more...
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
pycvt/__init__.py,sha256=6cwlC2WpWOqng9qk-CwqHc7QQQXdS9eBJb33Kd8NX-Q,75
|
|
2
|
+
pycvt/clolors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
pycvt/clolors/colors.py,sha256=5Xd7tT_Rqd8TLuCOPGOK_XpSbybwka95Y8c-7ORPSfU,2353
|
|
4
|
+
pycvt/paster/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
pycvt/paster/paste_image.py,sha256=MGhgiH1pVjHAPbFnXyWfRcjaDDY7eEr_JdwYTsvmE1A,1776
|
|
6
|
+
pycvt/static/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
pycvt/utils/__init__.py,sha256=VxWtYveyd5NMI2gULOkqmZxMpOqY_ALK-nHtcsHkWJ4,25
|
|
8
|
+
pycvt/utils/image_utils.py,sha256=VZFDPmZaISegmUyM1hGdcmAZytmYfG2SglLnhEEa2W0,4353
|
|
9
|
+
pycvt/vision/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
pycvt/vision/plot_boxes.py,sha256=ywhLIqwRk7zDMLtnDlHpp1DkKIxqAwRl_mt32iYNVG8,3103
|
|
11
|
+
pycvt/vision/utils.py,sha256=KJeD8d7vqTpfEht8YGdrGE6--tYwD-S86_kM5KqcCzg,917
|
|
12
|
+
pycvt-0.0.1.post1.dist-info/METADATA,sha256=5VomFw4_TblIT3AgYA6ZNsx3kVNfwwZYgE_yJWKQqq0,877
|
|
13
|
+
pycvt-0.0.1.post1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
+
pycvt-0.0.1.post1.dist-info/top_level.txt,sha256=GJFVu_yXlM6d0jjxa0u1kQNb-E2YRgpRDWgk2HRKI4s,6
|
|
15
|
+
pycvt-0.0.1.post1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pycvt
|