From f0b73d5fc14d6c7bc515a97cf9a2ecbb87875251 Mon Sep 17 00:00:00 2001 From: zcr Date: Wed, 15 Apr 2026 17:23:00 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8Ddesign=E5=8D=B0=E8=8A=B1?= =?UTF-8?q?=E9=83=A8=E5=88=86=20mask=5Finv=5Fprint=20=E6=8F=90=E5=8F=96?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pipeline/no_seg_print_painting.py | 44 +++++++++++++------ .../design_fast/pipeline/print_painting.py | 43 ++++++++++++------ 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/app/service/design_fast/pipeline/no_seg_print_painting.py b/app/service/design_fast/pipeline/no_seg_print_painting.py index 3341c81..0ffaa93 100644 --- a/app/service/design_fast/pipeline/no_seg_print_painting.py +++ b/app/service/design_fast/pipeline/no_seg_print_painting.py @@ -27,7 +27,7 @@ class NoSegPrintPainting: # 获取平铺 + 旋转 的overall print painting_dict = self.painting_collection(painting_dict, overall_print) result['no_seg_sketch_overall'] = result['no_seg_sketch_print'] = self.printpaint(result, painting_dict, print_=True) - result['pattern_image'] = result['no_seg_sketch_overall'] + # result['pattern_image'] = result['no_seg_sketch_overall'] if single_print: print_background = np.zeros((result['pattern_image'].shape[0], result['pattern_image'].shape[1], 3), dtype=np.uint8) @@ -166,15 +166,17 @@ class NoSegPrintPainting: dim_max = max(painting_dict['dim_image_h'], painting_dict['dim_image_w']) dim_pattern = (int(dim_max * print_['scale'] / 5), int(dim_max * print_['scale'] / 5)) gap = print_dict.get('gap', [[0, 0]])[0] - painting_dict['tile_print'] = tile_image(pattern=print_['image'], - dim=dim_pattern, - gap_x=gap[0], - gap_y=gap[1], - canvas_h=painting_dict['dim_image_h'], - canvas_w=painting_dict['dim_image_w'], - location=painting_dict['location'], - angle=int(print_.get('print_angle_list', [0])[0])) - painting_dict['mask_inv_print'] = np.zeros(painting_dict['tile_print'].shape[:2], dtype=np.uint8) + painting_dict['tile_print'], painting_dict['mask_inv_print'] = tile_image(pattern=print_['image'], + mask=print_['mask'], + dim=dim_pattern, + gap_x=gap[0], + gap_y=gap[1], + canvas_h=painting_dict['dim_image_h'], + canvas_w=painting_dict['dim_image_w'], + location=painting_dict['location'], + angle=int(print_.get('print_angle_list', [0])[0])) + # painting_dict['mask_inv_print'] = np.zeros(painting_dict['tile_print'].shape[:2], dtype=np.uint8) + # painting_dict['mask_inv_print'] = self.get_mask_inv(painting_dict['tile_print']) return painting_dict def tile_image(self, pattern, dim, scale, dim_image_h, dim_image_w, location, trigger=False): @@ -255,10 +257,15 @@ class NoSegPrintPainting: image = oss_get_image(oss_client=self.minio_client, bucket=bucket_name, object_name=object_name, data_type="PIL") # 判断图片格式,如果是RGBA 则贴在一张纯白图片上 防止透明转黑 if image.mode == "RGBA": + mask_pil = image.split()[3] new_background = Image.new('RGB', image.size, (255, 255, 255)) new_background.paste(image, mask=image.split()[3]) image = new_background + else: + mask_pil = Image.new('L', image.size, 255) # L=灰度图,255=纯白 print_dict['image'] = cv2.cvtColor(np.asarray(image), cv2.COLOR_RGB2BGR) + print_dict['mask'] = cv2.threshold(np.array(mask_pil), 127, 255, cv2.THRESH_BINARY)[1] + return print_dict def crop_image(self, image, image_size_h, image_size_w, location, print_shape): @@ -408,7 +415,7 @@ class NoSegPrintPainting: return cropped_img -def tile_image(pattern, dim, gap_x, gap_y, canvas_h, canvas_w, location, angle=0): +def tile_image(pattern, mask, dim, gap_x, gap_y, canvas_h, canvas_w, location, angle=0): """ 按照指定的 X/Y 间距平铺印花,并支持旋转 :param angle: 旋转角度 (度数, 逆时针) @@ -448,9 +455,20 @@ def tile_image(pattern, dim, gap_x, gap_y, canvas_h, canvas_w, location, angle=0 alpha_mask = cv2.merge([alpha_mask, alpha_mask, alpha_mask]) # 扩展到 3 通道 # 执行 Alpha 混合:结果 = 平铺层 * alpha + 背景 * (1 - alpha) - result = (tiled_bgr * alpha_mask + white_background * (1 - alpha_mask)).astype(np.uint8) + tiled_print = (tiled_bgr * alpha_mask + white_background * (1 - alpha_mask)).astype(np.uint8) - return result + resized_mask = cv2.resize(mask, dim, interpolation=cv2.INTER_NEAREST) # mask用最近邻,不模糊 + rotated_mask = rotate_image(resized_mask, angle) + + # 创建mask单元 + unit_mask = np.zeros((cell_h, cell_w), dtype=np.uint8) + unit_mask[:p_h, :p_w] = rotated_mask + + # 平铺mask + full_mask_tiled = np.tile(unit_mask, (tiles_y, tiles_x)) + tiled_mask = full_mask_tiled[offset_y: offset_y + canvas_h, offset_x: offset_x + canvas_w] + + return tiled_print, cv2.bitwise_not(tiled_mask) def rotate_image(image, angle): diff --git a/app/service/design_fast/pipeline/print_painting.py b/app/service/design_fast/pipeline/print_painting.py index fd54c59..c9f4653 100644 --- a/app/service/design_fast/pipeline/print_painting.py +++ b/app/service/design_fast/pipeline/print_painting.py @@ -41,7 +41,7 @@ class PrintPainting: if overall_print['print_path_list']: overall_print['location'][0] = [x * y for x, y in zip(overall_print['location'][0], result['resize_scale'])] painting_dict = {'dim_image_h': result['pattern_image'].shape[0], 'dim_image_w': result['pattern_image'].shape[1]} - result['print_image'] = result['pattern_image'] + result['print_image'] = result['pattern_image'].copy() # 获取平铺 + 旋转 的overall print painting_dict = self.painting_collection(painting_dict, overall_print) result['print_image'] = self.printpaint(result, painting_dict, print_=True) @@ -229,15 +229,15 @@ class PrintPainting: dim_max = max(painting_dict['dim_image_h'], painting_dict['dim_image_w']) dim_pattern = (int(dim_max * print_['scale'] / 5), int(dim_max * print_['scale'] / 5)) gap = print_dict.get('gap', [[0, 0]])[0] - painting_dict['tile_print'] = tile_image(pattern=print_['image'], - dim=dim_pattern, - gap_x=gap[0], - gap_y=gap[1], - canvas_h=painting_dict['dim_image_h'], - canvas_w=painting_dict['dim_image_w'], - location=painting_dict['location'], - angle=int(print_.get('print_angle_list', [0])[0])) - painting_dict['mask_inv_print'] = np.zeros(painting_dict['tile_print'].shape[:2], dtype=np.uint8) + painting_dict['tile_print'], painting_dict['mask_inv_print'] = tile_image(pattern=print_['image'], + mask=print_['mask'], + dim=dim_pattern, + gap_x=gap[0], + gap_y=gap[1], + canvas_h=painting_dict['dim_image_h'], + canvas_w=painting_dict['dim_image_w'], + location=painting_dict['location'], + angle=int(print_.get('print_angle_list', [0])[0])) return painting_dict def tile_image(self, pattern, dim, scale, dim_image_h, dim_image_w, location, trigger=False): @@ -318,10 +318,15 @@ class PrintPainting: image = oss_get_image(oss_client=self.minio_client, bucket=bucket_name, object_name=object_name, data_type="PIL") # 判断图片格式,如果是RGBA 则贴在一张纯白图片上 防止透明转黑 if image.mode == "RGBA": + mask_pil = image.split()[3] new_background = Image.new('RGB', image.size, (255, 255, 255)) new_background.paste(image, mask=image.split()[3]) image = new_background + else: + mask_pil = Image.new('L', image.size, 255) # L=灰度图,255=纯白 print_dict['image'] = cv2.cvtColor(np.asarray(image), cv2.COLOR_RGB2BGR) + print_dict['mask'] = cv2.threshold(np.array(mask_pil), 127, 255, cv2.THRESH_BINARY)[1] + return print_dict def crop_image(self, image, image_size_h, image_size_w, location, print_shape): @@ -471,7 +476,7 @@ class PrintPainting: return cropped_img -def tile_image(pattern, dim, gap_x, gap_y, canvas_h, canvas_w, location, angle=0): +def tile_image(pattern, mask, dim, gap_x, gap_y, canvas_h, canvas_w, location, angle=0): """ 按照指定的 X/Y 间距平铺印花,并支持旋转 :param angle: 旋转角度 (度数, 逆时针) @@ -511,9 +516,21 @@ def tile_image(pattern, dim, gap_x, gap_y, canvas_h, canvas_w, location, angle=0 alpha_mask = cv2.merge([alpha_mask, alpha_mask, alpha_mask]) # 扩展到 3 通道 # 执行 Alpha 混合:结果 = 平铺层 * alpha + 背景 * (1 - alpha) - result = (tiled_bgr * alpha_mask + white_background * (1 - alpha_mask)).astype(np.uint8) + tiled_print = (tiled_bgr * alpha_mask + white_background * (1 - alpha_mask)).astype(np.uint8) - return result + # 处理mask的平铺 + resized_mask = cv2.resize(mask, dim, interpolation=cv2.INTER_NEAREST) # mask用最近邻,不模糊 + rotated_mask = rotate_image(resized_mask, angle) + + # 创建mask单元 + unit_mask = np.zeros((cell_h, cell_w), dtype=np.uint8) + unit_mask[:p_h, :p_w] = rotated_mask + + # 平铺mask + full_mask_tiled = np.tile(unit_mask, (tiles_y, tiles_x)) + tiled_mask = full_mask_tiled[offset_y: offset_y + canvas_h, offset_x: offset_x + canvas_w] + + return tiled_print, cv2.bitwise_not(tiled_mask) def rotate_image(image, angle): From 6892361050091c82dd1dc3f6c84de8f6b52fad56 Mon Sep 17 00:00:00 2001 From: zcr Date: Wed, 15 Apr 2026 17:36:29 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8Ddesign=E5=8D=B0=E8=8A=B1?= =?UTF-8?q?=E9=83=A8=E5=88=86=20overall=20=E6=A8=A1=E5=BC=8F=E5=8D=B0?= =?UTF-8?q?=E8=8A=B1=E5=B9=B3=E9=93=BA=E8=B5=B7=E5=A7=8B=E4=BB=8E=E5=8D=B0?= =?UTF-8?q?=E8=8A=B1=E5=9B=BE=E7=89=87=E4=B8=AD=E5=BF=83=E5=BC=80=E5=A7=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pipeline/no_seg_print_painting.py | 51 +++++++++++------- .../design_fast/pipeline/print_painting.py | 52 +++++++++++-------- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/app/service/design_fast/pipeline/no_seg_print_painting.py b/app/service/design_fast/pipeline/no_seg_print_painting.py index 0ffaa93..a1db587 100644 --- a/app/service/design_fast/pipeline/no_seg_print_painting.py +++ b/app/service/design_fast/pipeline/no_seg_print_painting.py @@ -418,6 +418,9 @@ class NoSegPrintPainting: def tile_image(pattern, mask, dim, gap_x, gap_y, canvas_h, canvas_w, location, angle=0): """ 按照指定的 X/Y 间距平铺印花,并支持旋转 + 【修改版】以被平铺图案的【中心】作为平铺基准点 + + :param location: [[center_y, center_x]] → 第一个图案中心的坐标 :param angle: 旋转角度 (度数, 逆时针) """ # 1. 确保输入是 RGBA @@ -429,44 +432,52 @@ def tile_image(pattern, mask, dim, gap_x, gap_y, canvas_h, canvas_w, location, a rotated_p = rotate_image(resized_p, angle) p_h, p_w = rotated_p.shape[:2] - # 3. 创建透明单元格 - cell_h, cell_w = p_h + gap_y, p_w + gap_x + # 3. 创建透明单元格(图案放在单元格中心) + cell_h = p_h + gap_y + cell_w = p_w + gap_x + unit_cell = np.zeros((cell_h, cell_w, 4), dtype=np.uint8) - unit_cell[:p_h, :p_w, :] = rotated_p + + # 计算图案在单元格中的左上角位置(让图案居中) + start_y = (cell_h - p_h) // 2 + start_x = (cell_w - p_w) // 2 + unit_cell[start_y:start_y + p_h, start_x:start_x + p_w, :] = rotated_p # 4. 执行平铺 - tiles_y = (canvas_h // cell_h) + 2 - tiles_x = (canvas_w // cell_w) + 2 + tiles_y = (canvas_h // cell_h) + 3 # 多加一点余量更安全 + tiles_x = (canvas_w // cell_w) + 3 full_tiled = np.tile(unit_cell, (tiles_y, tiles_x, 1)) - # 5. 裁剪平铺层 - offset_x = int(location[0][1] % cell_w) - offset_y = int(location[0][0] % cell_h) + # 5. 计算偏移(关键修改:以中心为基准) + center_y, center_x = location[0][0], location[0][1] # 第一个图案的中心位置 + + # 计算从哪个位置开始裁剪,才能让中心落在指定坐标 + offset_y = int((center_y - (p_h // 2)) % cell_h) + offset_x = int((center_x - (p_w // 2)) % cell_w) + tiled_layer = full_tiled[offset_y: offset_y + canvas_h, offset_x: offset_x + canvas_w] - # 6. 创建纯白色背景并合成 - # 创建一个纯白色的 BGR 画布 + # 6. 创建纯白色背景并合成(保持你原来的风格) white_background = np.full((canvas_h, canvas_w, 3), 255, dtype=np.uint8) - # 分离平铺层的颜色通道和 Alpha 通道 tiled_bgr = tiled_layer[:, :, :3] - alpha_mask = tiled_layer[:, :, 3] / 255.0 # 归一化到 0-1 - alpha_mask = cv2.merge([alpha_mask, alpha_mask, alpha_mask]) # 扩展到 3 通道 + alpha_mask = tiled_layer[:, :, 3] / 255.0 + alpha_mask = cv2.merge([alpha_mask, alpha_mask, alpha_mask]) - # 执行 Alpha 混合:结果 = 平铺层 * alpha + 背景 * (1 - alpha) tiled_print = (tiled_bgr * alpha_mask + white_background * (1 - alpha_mask)).astype(np.uint8) - resized_mask = cv2.resize(mask, dim, interpolation=cv2.INTER_NEAREST) # mask用最近邻,不模糊 - rotated_mask = rotate_image(resized_mask, angle) + # ====================== 处理 Mask ====================== + # Mask 也同样居中处理 + resized_mask = cv2.resize(mask, dim, interpolation=cv2.INTER_NEAREST) + rotated_mask = rotate_image(resized_mask, angle) # 注意:mask也需要旋转 - # 创建mask单元 unit_mask = np.zeros((cell_h, cell_w), dtype=np.uint8) - unit_mask[:p_h, :p_w] = rotated_mask + unit_mask[start_y:start_y + p_h, start_x:start_x + p_w] = rotated_mask - # 平铺mask full_mask_tiled = np.tile(unit_mask, (tiles_y, tiles_x)) - tiled_mask = full_mask_tiled[offset_y: offset_y + canvas_h, offset_x: offset_x + canvas_w] + tiled_mask = full_mask_tiled[offset_y: offset_y + canvas_h, + offset_x: offset_x + canvas_w] return tiled_print, cv2.bitwise_not(tiled_mask) diff --git a/app/service/design_fast/pipeline/print_painting.py b/app/service/design_fast/pipeline/print_painting.py index c9f4653..ebf5856 100644 --- a/app/service/design_fast/pipeline/print_painting.py +++ b/app/service/design_fast/pipeline/print_painting.py @@ -479,6 +479,9 @@ class PrintPainting: def tile_image(pattern, mask, dim, gap_x, gap_y, canvas_h, canvas_w, location, angle=0): """ 按照指定的 X/Y 间距平铺印花,并支持旋转 + 【修改版】以被平铺图案的【中心】作为平铺基准点 + + :param location: [[center_y, center_x]] → 第一个图案中心的坐标 :param angle: 旋转角度 (度数, 逆时针) """ # 1. 确保输入是 RGBA @@ -490,45 +493,52 @@ def tile_image(pattern, mask, dim, gap_x, gap_y, canvas_h, canvas_w, location, a rotated_p = rotate_image(resized_p, angle) p_h, p_w = rotated_p.shape[:2] - # 3. 创建透明单元格 - cell_h, cell_w = p_h + gap_y, p_w + gap_x + # 3. 创建透明单元格(图案放在单元格中心) + cell_h = p_h + gap_y + cell_w = p_w + gap_x + unit_cell = np.zeros((cell_h, cell_w, 4), dtype=np.uint8) - unit_cell[:p_h, :p_w, :] = rotated_p + + # 计算图案在单元格中的左上角位置(让图案居中) + start_y = (cell_h - p_h) // 2 + start_x = (cell_w - p_w) // 2 + unit_cell[start_y:start_y + p_h, start_x:start_x + p_w, :] = rotated_p # 4. 执行平铺 - tiles_y = (canvas_h // cell_h) + 2 - tiles_x = (canvas_w // cell_w) + 2 + tiles_y = (canvas_h // cell_h) + 3 # 多加一点余量更安全 + tiles_x = (canvas_w // cell_w) + 3 full_tiled = np.tile(unit_cell, (tiles_y, tiles_x, 1)) - # 5. 裁剪平铺层 - offset_x = int(location[0][1] % cell_w) - offset_y = int(location[0][0] % cell_h) + # 5. 计算偏移(关键修改:以中心为基准) + center_y, center_x = location[0][0], location[0][1] # 第一个图案的中心位置 + + # 计算从哪个位置开始裁剪,才能让中心落在指定坐标 + offset_y = int((center_y - (p_h // 2)) % cell_h) + offset_x = int((center_x - (p_w // 2)) % cell_w) + tiled_layer = full_tiled[offset_y: offset_y + canvas_h, offset_x: offset_x + canvas_w] - # 6. 创建纯白色背景并合成 - # 创建一个纯白色的 BGR 画布 + # 6. 创建纯白色背景并合成(保持你原来的风格) white_background = np.full((canvas_h, canvas_w, 3), 255, dtype=np.uint8) - # 分离平铺层的颜色通道和 Alpha 通道 tiled_bgr = tiled_layer[:, :, :3] - alpha_mask = tiled_layer[:, :, 3] / 255.0 # 归一化到 0-1 - alpha_mask = cv2.merge([alpha_mask, alpha_mask, alpha_mask]) # 扩展到 3 通道 + alpha_mask = tiled_layer[:, :, 3] / 255.0 + alpha_mask = cv2.merge([alpha_mask, alpha_mask, alpha_mask]) - # 执行 Alpha 混合:结果 = 平铺层 * alpha + 背景 * (1 - alpha) tiled_print = (tiled_bgr * alpha_mask + white_background * (1 - alpha_mask)).astype(np.uint8) - # 处理mask的平铺 - resized_mask = cv2.resize(mask, dim, interpolation=cv2.INTER_NEAREST) # mask用最近邻,不模糊 - rotated_mask = rotate_image(resized_mask, angle) + # ====================== 处理 Mask ====================== + # Mask 也同样居中处理 + resized_mask = cv2.resize(mask, dim, interpolation=cv2.INTER_NEAREST) + rotated_mask = rotate_image(resized_mask, angle) # 注意:mask也需要旋转 - # 创建mask单元 unit_mask = np.zeros((cell_h, cell_w), dtype=np.uint8) - unit_mask[:p_h, :p_w] = rotated_mask + unit_mask[start_y:start_y + p_h, start_x:start_x + p_w] = rotated_mask - # 平铺mask full_mask_tiled = np.tile(unit_mask, (tiles_y, tiles_x)) - tiled_mask = full_mask_tiled[offset_y: offset_y + canvas_h, offset_x: offset_x + canvas_w] + tiled_mask = full_mask_tiled[offset_y: offset_y + canvas_h, + offset_x: offset_x + canvas_w] return tiled_print, cv2.bitwise_not(tiled_mask)