TheEeeeLin commited on
Commit
80a80fa
1 Parent(s): 57a7924
demo/locales.py CHANGED
@@ -685,4 +685,18 @@ LOCALES = {
685
  "choices": ["시작"],
686
  },
687
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
  }
 
685
  "choices": ["시작"],
686
  },
687
  },
688
+ "template_photo": {
689
+ "en": {
690
+ "label": "Social Media Template Photo",
691
+ },
692
+ "zh": {
693
+ "label": "社交媒体模版照",
694
+ },
695
+ "ja": {
696
+ "label": "SNS テンプレート写真",
697
+ },
698
+ "ko": {
699
+ "label": "SNS 템플릿 사진",
700
+ },
701
+ },
702
  }
demo/processor.py CHANGED
@@ -12,6 +12,7 @@ from hivision.creator.layout_calculator import (
12
  generate_layout_image,
13
  )
14
  from hivision.creator.choose_handler import choose_handler
 
15
  from demo.utils import range_check
16
  import gradio as gr
17
  import os
@@ -141,6 +142,7 @@ class IDPhotoProcessor:
141
  watermark_text_color,
142
  )
143
 
 
144
  def _initialize_idphoto_json(
145
  self,
146
  mode_option,
@@ -158,6 +160,7 @@ class IDPhotoProcessor:
158
  "custom_image_dpi": None,
159
  }
160
 
 
161
  def _process_size_mode(
162
  self,
163
  idphoto_json,
@@ -202,6 +205,7 @@ class IDPhotoProcessor:
202
  else:
203
  idphoto_json["size"] = (None, None)
204
 
 
205
  def _process_color_mode(
206
  self,
207
  idphoto_json,
@@ -231,13 +235,16 @@ class IDPhotoProcessor:
231
  int(hex_color[i : i + 2], 16) for i in (0, 2, 4)
232
  )
233
  else:
234
- raise ValueError("Invalid hex color. You can only use 6 or 7 characters. For example: #FFFFFF or FFFFFF")
 
 
235
  else:
236
  hex_color = LOCALES["bg_color"][language]["develop"][color_option]
237
  idphoto_json["color_bgr"] = tuple(
238
  int(hex_color[i : i + 2], 16) for i in (0, 2, 4)
239
  )
240
 
 
241
  def _generate_id_photo(
242
  self,
243
  creator: IDCreator,
@@ -272,6 +279,7 @@ class IDPhotoProcessor:
272
  face_alignment=face_alignment_option,
273
  )
274
 
 
275
  def _handle_photo_generation_error(self, language):
276
  """处理照片生成错误"""
277
  return [gr.update(value=None) for _ in range(4)] + [
@@ -282,6 +290,7 @@ class IDPhotoProcessor:
282
  None,
283
  ]
284
 
 
285
  def _process_generated_photo(
286
  self,
287
  result,
@@ -305,20 +314,6 @@ class IDPhotoProcessor:
305
  result_image_standard, result_image_hd, idphoto_json
306
  )
307
 
308
- # 生成排版照片
309
- result_image_layout, result_image_layout_visible = self._generate_image_layout(
310
- idphoto_json,
311
- result_image_standard,
312
- language,
313
- watermark_option,
314
- watermark_text,
315
- watermark_text_size,
316
- watermark_text_opacity,
317
- watermark_text_angle,
318
- watermark_text_space,
319
- watermark_text_color,
320
- )
321
-
322
  # 添加水印
323
  if watermark_option == LOCALES["watermark_switch"][language]["choices"][1]:
324
  result_image_standard, result_image_hd = self._add_watermark(
@@ -331,6 +326,20 @@ class IDPhotoProcessor:
331
  watermark_text_space,
332
  watermark_text_color,
333
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
  # 调整图片大小
336
  output_image_path_dict = self._resize_image_if_needed(
@@ -348,6 +357,7 @@ class IDPhotoProcessor:
348
  result_image_standard_png,
349
  result_image_hd_png,
350
  gr.update(value=result_image_layout, visible=result_image_layout_visible),
 
351
  )
352
  else:
353
  # 如果output_image_path_dict不为None,
@@ -362,13 +372,15 @@ class IDPhotoProcessor:
362
  (
363
  output_image_path_dict["hd"]["path"]
364
  if output_image_path_dict["hd"]["processed"]
365
- else result_image_hd
366
  ),
367
  result_image_standard_png,
368
  result_image_hd_png,
369
  gr.update(value=result_image_layout, visible=result_image_layout_visible),
 
370
  )
371
 
 
372
  def _render_background(self, result_image_standard, result_image_hd, idphoto_json):
373
  """渲染背景"""
374
  render_modes = {0: "pure_color", 1: "updown_gradient", 2: "center_gradient"}
@@ -387,21 +399,15 @@ class IDPhotoProcessor:
387
 
388
  return result_image_standard, result_image_hd
389
 
 
390
  def _generate_image_layout(
391
  self,
392
  idphoto_json,
393
  result_image_standard,
394
  language,
395
- watermark_option,
396
- watermark_text,
397
- watermark_text_size,
398
- watermark_text_opacity,
399
- watermark_text_angle,
400
- watermark_text_space,
401
- watermark_text_color,
402
  ):
403
  """生成排版照片"""
404
- # 如果选择了自定义尺寸,则不生成排版照片
405
  if idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]:
406
  return None, False
407
 
@@ -409,21 +415,9 @@ class IDPhotoProcessor:
409
  input_height=idphoto_json["size"][0],
410
  input_width=idphoto_json["size"][1],
411
  )
412
-
413
- image = result_image_standard
414
- if watermark_option == LOCALES["watermark_switch"][language]["choices"][1]:
415
- image = add_watermark(
416
- image=image,
417
- text=watermark_text,
418
- size=watermark_text_size,
419
- opacity=watermark_text_opacity,
420
- angle=watermark_text_angle,
421
- space=watermark_text_space,
422
- color=watermark_text_color,
423
- )
424
-
425
  result_image_layout = generate_layout_image(
426
- image,
427
  typography_arr,
428
  typography_rotate,
429
  height=idphoto_json["size"][0],
@@ -432,6 +426,28 @@ class IDPhotoProcessor:
432
 
433
  return result_image_layout, True
434
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
  def _add_watermark(
436
  self,
437
  result_image_standard,
@@ -472,7 +488,10 @@ class IDPhotoProcessor:
472
  )
473
  timestamp = int(time.time())
474
  output_paths = {
475
- "standard": {"path": f"{base_path}/{timestamp}_standard", "processed": False},
 
 
 
476
  "hd": {"path": f"{base_path}/{timestamp}_hd", "processed": False},
477
  "layout": {"path": f"{base_path}/{timestamp}_layout", "processed": False},
478
  }
@@ -500,9 +519,11 @@ class IDPhotoProcessor:
500
  )
501
  output_paths["standard"]["processed"] = True
502
  # 保存高清图像和排版图像
503
- save_image_dpi_to_bytes(result_image_hd, output_paths["hd"]["path"], dpi=custom_dpi)
 
 
504
  output_paths["hd"]["processed"] = True
505
- if result_image_layout:
506
  save_image_dpi_to_bytes(
507
  result_image_layout, output_paths["layout"]["path"], dpi=custom_dpi
508
  )
@@ -519,7 +540,9 @@ class IDPhotoProcessor:
519
  pass
520
  else:
521
  save_image_dpi_to_bytes(
522
- locals()[f"result_image_{key}"], output_paths[key]["path"], dpi=custom_dpi
 
 
523
  )
524
  output_paths[key]["processed"] = True
525
 
@@ -548,7 +571,8 @@ class IDPhotoProcessor:
548
  result_image_hd,
549
  result_image_standard_png,
550
  result_image_hd_png,
551
- result_layout_image,
 
552
  ):
553
  """创建响应"""
554
  response = [
@@ -556,7 +580,8 @@ class IDPhotoProcessor:
556
  result_image_hd,
557
  result_image_standard_png,
558
  result_image_hd_png,
559
- result_layout_image,
 
560
  gr.update(visible=False),
561
  ]
562
 
 
12
  generate_layout_image,
13
  )
14
  from hivision.creator.choose_handler import choose_handler
15
+ from hivision.plugin.template.template_calculator import generte_template_photo
16
  from demo.utils import range_check
17
  import gradio as gr
18
  import os
 
142
  watermark_text_color,
143
  )
144
 
145
+ # 初始化idphoto_json字典
146
  def _initialize_idphoto_json(
147
  self,
148
  mode_option,
 
160
  "custom_image_dpi": None,
161
  }
162
 
163
+ # 处理尺寸模式
164
  def _process_size_mode(
165
  self,
166
  idphoto_json,
 
205
  else:
206
  idphoto_json["size"] = (None, None)
207
 
208
+ # 处理颜色模式
209
  def _process_color_mode(
210
  self,
211
  idphoto_json,
 
235
  int(hex_color[i : i + 2], 16) for i in (0, 2, 4)
236
  )
237
  else:
238
+ raise ValueError(
239
+ "Invalid hex color. You can only use 6 or 7 characters. For example: #FFFFFF or FFFFFF"
240
+ )
241
  else:
242
  hex_color = LOCALES["bg_color"][language]["develop"][color_option]
243
  idphoto_json["color_bgr"] = tuple(
244
  int(hex_color[i : i + 2], 16) for i in (0, 2, 4)
245
  )
246
 
247
+ # 生成证件照
248
  def _generate_id_photo(
249
  self,
250
  creator: IDCreator,
 
279
  face_alignment=face_alignment_option,
280
  )
281
 
282
+ # 处理照片生成错误
283
  def _handle_photo_generation_error(self, language):
284
  """处理照片生成错误"""
285
  return [gr.update(value=None) for _ in range(4)] + [
 
290
  None,
291
  ]
292
 
293
+ # 处理生成的照片
294
  def _process_generated_photo(
295
  self,
296
  result,
 
314
  result_image_standard, result_image_hd, idphoto_json
315
  )
316
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  # 添加水印
318
  if watermark_option == LOCALES["watermark_switch"][language]["choices"][1]:
319
  result_image_standard, result_image_hd = self._add_watermark(
 
326
  watermark_text_space,
327
  watermark_text_color,
328
  )
329
+
330
+ # 生成排版照片
331
+ result_image_layout, result_image_layout_visible = self._generate_image_layout(
332
+ idphoto_json,
333
+ result_image_standard,
334
+ language,
335
+ )
336
+
337
+ # 生成模板照片
338
+ result_image_template, result_image_template_visible = self._generate_image_template(
339
+ idphoto_json,
340
+ result_image_hd,
341
+ language,
342
+ )
343
 
344
  # 调整图片大小
345
  output_image_path_dict = self._resize_image_if_needed(
 
357
  result_image_standard_png,
358
  result_image_hd_png,
359
  gr.update(value=result_image_layout, visible=result_image_layout_visible),
360
+ gr.update(value=result_image_template, visible=result_image_template_visible),
361
  )
362
  else:
363
  # 如果output_image_path_dict不为None,
 
372
  (
373
  output_image_path_dict["hd"]["path"]
374
  if output_image_path_dict["hd"]["processed"]
375
+ else result_image_hd
376
  ),
377
  result_image_standard_png,
378
  result_image_hd_png,
379
  gr.update(value=result_image_layout, visible=result_image_layout_visible),
380
+ gr.update(value=result_image_template, visible=result_image_template_visible),
381
  )
382
 
383
+ # 渲染背景
384
  def _render_background(self, result_image_standard, result_image_hd, idphoto_json):
385
  """渲染背景"""
386
  render_modes = {0: "pure_color", 1: "updown_gradient", 2: "center_gradient"}
 
399
 
400
  return result_image_standard, result_image_hd
401
 
402
+ # 生成排版照片
403
  def _generate_image_layout(
404
  self,
405
  idphoto_json,
406
  result_image_standard,
407
  language,
 
 
 
 
 
 
 
408
  ):
409
  """生成排版照片"""
410
+ # 如果选择了只换底,则不生成排版照片
411
  if idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]:
412
  return None, False
413
 
 
415
  input_height=idphoto_json["size"][0],
416
  input_width=idphoto_json["size"][1],
417
  )
418
+
 
 
 
 
 
 
 
 
 
 
 
 
419
  result_image_layout = generate_layout_image(
420
+ result_image_standard,
421
  typography_arr,
422
  typography_rotate,
423
  height=idphoto_json["size"][0],
 
426
 
427
  return result_image_layout, True
428
 
429
+ def _generate_image_template(
430
+ self,
431
+ idphoto_json,
432
+ result_image_hd,
433
+ language,
434
+ ):
435
+ # 如果选择了只换底,则不生成模板照片
436
+ if idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]:
437
+ return None, False
438
+
439
+ TEMPLATE_NAME_LIST = ["template_1", "template_2"]
440
+ """生成模板照片"""
441
+ result_image_template_list = []
442
+ for template_name in TEMPLATE_NAME_LIST:
443
+ result_image_template = generte_template_photo(
444
+ template_name=template_name,
445
+ input_image=result_image_hd,
446
+ )
447
+ result_image_template_list.append(result_image_template)
448
+ return result_image_template_list, True
449
+
450
+ # 添加水印
451
  def _add_watermark(
452
  self,
453
  result_image_standard,
 
488
  )
489
  timestamp = int(time.time())
490
  output_paths = {
491
+ "standard": {
492
+ "path": f"{base_path}/{timestamp}_standard",
493
+ "processed": False,
494
+ },
495
  "hd": {"path": f"{base_path}/{timestamp}_hd", "processed": False},
496
  "layout": {"path": f"{base_path}/{timestamp}_layout", "processed": False},
497
  }
 
519
  )
520
  output_paths["standard"]["processed"] = True
521
  # 保存高清图像和排版图像
522
+ save_image_dpi_to_bytes(
523
+ result_image_hd, output_paths["hd"]["path"], dpi=custom_dpi
524
+ )
525
  output_paths["hd"]["processed"] = True
526
+ if result_image_layout is not None:
527
  save_image_dpi_to_bytes(
528
  result_image_layout, output_paths["layout"]["path"], dpi=custom_dpi
529
  )
 
540
  pass
541
  else:
542
  save_image_dpi_to_bytes(
543
+ locals()[f"result_image_{key}"],
544
+ output_paths[key]["path"],
545
+ dpi=custom_dpi,
546
  )
547
  output_paths[key]["processed"] = True
548
 
 
571
  result_image_hd,
572
  result_image_standard_png,
573
  result_image_hd_png,
574
+ result_layout_image_gr,
575
+ result_image_template_gr,
576
  ):
577
  """创建响应"""
578
  response = [
 
580
  result_image_hd,
581
  result_image_standard_png,
582
  result_image_hd_png,
583
+ result_layout_image_gr,
584
+ result_image_template_gr,
585
  gr.update(visible=False),
586
  ]
587
 
demo/ui.py CHANGED
@@ -349,27 +349,34 @@ def create_ui(
349
  label=LOCALES["notification"][DEFAULT_LANG]["label"], visible=False
350
  )
351
  with gr.Row():
 
352
  img_output_standard = gr.Image(
353
  label=LOCALES["standard_photo"][DEFAULT_LANG]["label"],
354
  height=350,
355
  format="jpeg",
356
  )
 
357
  img_output_standard_hd = gr.Image(
358
  label=LOCALES["hd_photo"][DEFAULT_LANG]["label"],
359
  height=350,
360
  format="jpeg",
361
  )
362
-
363
  img_output_layout = gr.Image(
364
  label=LOCALES["layout_photo"][DEFAULT_LANG]["label"],
365
  height=350,
366
  format="jpeg",
367
  )
368
-
369
- file_download = gr.File(
370
- label=LOCALES["download"][DEFAULT_LANG]["label"], visible=False
371
- )
372
-
 
 
 
 
 
373
  with gr.Accordion(
374
  LOCALES["matting_image"][DEFAULT_LANG]["label"], open=False
375
  ) as matting_image_accordion:
@@ -443,9 +450,6 @@ def create_ui(
443
  img_output_layout: gr.update(
444
  label=LOCALES["layout_photo"][language]["label"]
445
  ),
446
- file_download: gr.update(
447
- label=LOCALES["download"][language]["label"]
448
- ),
449
  head_measure_ratio_option: gr.update(
450
  label=LOCALES["head_measure_ratio"][language]["label"]
451
  ),
@@ -530,6 +534,12 @@ def create_ui(
530
  custom_size_height_mm: gr.update(
531
  label=LOCALES["custom_size_mm"][language]["height"]
532
  ),
 
 
 
 
 
 
533
  }
534
 
535
  def change_visibility(option, lang, locales_key, custom_component):
@@ -612,7 +622,6 @@ def create_ui(
612
  img_output_standard_png,
613
  img_output_standard_hd_png,
614
  img_output_layout,
615
- file_download,
616
  head_measure_ratio_option,
617
  top_distance_option,
618
  key_parameter_tab,
@@ -639,6 +648,8 @@ def create_ui(
639
  custom_size_height_px,
640
  custom_size_width_mm,
641
  custom_size_height_mm,
 
 
642
  ],
643
  )
644
 
@@ -721,6 +732,7 @@ def create_ui(
721
  img_output_standard_png,
722
  img_output_standard_hd_png,
723
  img_output_layout,
 
724
  notification,
725
  ],
726
  )
 
349
  label=LOCALES["notification"][DEFAULT_LANG]["label"], visible=False
350
  )
351
  with gr.Row():
352
+ # 标准照
353
  img_output_standard = gr.Image(
354
  label=LOCALES["standard_photo"][DEFAULT_LANG]["label"],
355
  height=350,
356
  format="jpeg",
357
  )
358
+ # 高清照
359
  img_output_standard_hd = gr.Image(
360
  label=LOCALES["hd_photo"][DEFAULT_LANG]["label"],
361
  height=350,
362
  format="jpeg",
363
  )
364
+ # 排版照
365
  img_output_layout = gr.Image(
366
  label=LOCALES["layout_photo"][DEFAULT_LANG]["label"],
367
  height=350,
368
  format="jpeg",
369
  )
370
+ # 模版照片
371
+ with gr.Accordion(
372
+ LOCALES["template_photo"][DEFAULT_LANG]["label"], open=False
373
+ ) as template_image_accordion:
374
+ img_output_template = gr.Gallery(
375
+ label=LOCALES["template_photo"][DEFAULT_LANG]["label"],
376
+ height=350,
377
+ format="jpeg",
378
+ )
379
+ # 抠图图像
380
  with gr.Accordion(
381
  LOCALES["matting_image"][DEFAULT_LANG]["label"], open=False
382
  ) as matting_image_accordion:
 
450
  img_output_layout: gr.update(
451
  label=LOCALES["layout_photo"][language]["label"]
452
  ),
 
 
 
453
  head_measure_ratio_option: gr.update(
454
  label=LOCALES["head_measure_ratio"][language]["label"]
455
  ),
 
534
  custom_size_height_mm: gr.update(
535
  label=LOCALES["custom_size_mm"][language]["height"]
536
  ),
537
+ img_output_template: gr.update(
538
+ label=LOCALES["template_photo"][language]["label"]
539
+ ),
540
+ template_image_accordion: gr.update(
541
+ label=LOCALES["template_photo"][language]["label"]
542
+ ),
543
  }
544
 
545
  def change_visibility(option, lang, locales_key, custom_component):
 
622
  img_output_standard_png,
623
  img_output_standard_hd_png,
624
  img_output_layout,
 
625
  head_measure_ratio_option,
626
  top_distance_option,
627
  key_parameter_tab,
 
648
  custom_size_height_px,
649
  custom_size_width_mm,
650
  custom_size_height_mm,
651
+ img_output_template,
652
+ template_image_accordion,
653
  ],
654
  )
655
 
 
732
  img_output_standard_png,
733
  img_output_standard_hd_png,
734
  img_output_layout,
735
+ img_output_template,
736
  notification,
737
  ],
738
  )
hivision/plugin/template/assets/template_1.png ADDED
hivision/plugin/template/assets/template_2.png ADDED
hivision/plugin/template/assets/template_config.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "template_1": {
3
+ "width": 1080,
4
+ "height": 1400,
5
+ "anchor_points": {
6
+ "left_top": [358, 153],
7
+ "right_top": [1017, 353],
8
+ "left_bottom": [56, 1134],
9
+ "right_bottom": [747, 1332],
10
+ "rotation": -16.42
11
+ }
12
+ },
13
+ "template_2": {
14
+ "width": 1080,
15
+ "height": 1440,
16
+ "anchor_points": {
17
+ "left_top": [199, 199],
18
+ "right_top": [921, 216],
19
+ "left_bottom": [163, 1129],
20
+ "right_bottom": [876, 1153],
21
+ "rotation": -2.2
22
+ }
23
+ }
24
+ }
hivision/plugin/template/template_calculator.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import json
4
+ from rotation_adjust import rotate_bound
5
+ import os
6
+
7
+ base_path = os.path.dirname(os.path.abspath(__file__))
8
+ template_config_path = os.path.join(base_path, 'assets', 'template_config.json')
9
+
10
+ def generte_template_photo(template_name: str, input_image: np.ndarray) -> np.ndarray:
11
+ """
12
+ 生成模板照片
13
+ :param template_name: 模板名称
14
+ :param input_image: 输入图像
15
+ :return: 模板照片
16
+ """
17
+ # 读取模板配置json
18
+ with open(template_config_path, 'r') as f:
19
+ template_config_dict = json.load(f)
20
+ # 获取对应该模板的配置
21
+ template_config = template_config_dict[template_name]
22
+
23
+ template_width = template_config['width']
24
+ template_height = template_config['height']
25
+
26
+ anchor_points = template_config['anchor_points']
27
+ rotation = anchor_points['rotation']
28
+ left_top = anchor_points['left_top']
29
+ right_top = anchor_points['right_top']
30
+ left_bottom = anchor_points['left_bottom']
31
+ right_bottom = anchor_points['right_bottom']
32
+
33
+ if rotation < 0:
34
+ height = right_bottom[1] - left_top[1]
35
+ width = right_top[0] - left_bottom[0]
36
+ else:
37
+ height = left_top[1] - right_bottom[1]
38
+ width = left_bottom[0] - right_top[0]
39
+
40
+ # 读取模板图像
41
+ template_image_path = os.path.join(base_path, 'assets', f'{template_name}.png')
42
+ template_image = cv2.imread(template_image_path, cv2.IMREAD_UNCHANGED)
43
+
44
+ # 无损旋转
45
+ rotated_image = rotate_bound(input_image, -1 * rotation)[0]
46
+ rotated_image_height, rotated_image_width, _ = rotated_image.shape
47
+
48
+ # 计算缩放比例
49
+ scale_x = width / rotated_image_width
50
+ scale_y = height / rotated_image_height
51
+ scale = max(scale_x, scale_y)
52
+
53
+ resized_image = cv2.resize(rotated_image, None, fx=scale, fy=scale)
54
+ resized_height, resized_width, _ = resized_image.shape
55
+
56
+ # 创建一个与template_image大小相同的背景,使用白色填充
57
+ result = np.full((template_height, template_width, 3), 255, dtype=np.uint8)
58
+
59
+ # 计算粘贴位置
60
+ paste_x = left_bottom[0]
61
+ paste_y = left_top[1]
62
+
63
+ # 确保不会超出边界
64
+ paste_height = min(resized_height, template_height - paste_y)
65
+ paste_width = min(resized_width, template_width - paste_x)
66
+
67
+ # 将旋转后的图像粘贴到结果图像上
68
+ result[paste_y:paste_y+paste_height, paste_x:paste_x+paste_width] = resized_image[:paste_height, :paste_width]
69
+
70
+ template_image = cv2.cvtColor(template_image, cv2.COLOR_BGRA2RGBA)
71
+
72
+ # 将template_image叠加到结果图像上
73
+ if template_image.shape[2] == 4: # 确保template_image有alpha通道
74
+ alpha = template_image[:, :, 3] / 255.0
75
+ for c in range(0, 3):
76
+ result[:, :, c] = result[:, :, c] * (1 - alpha) + template_image[:, :, c] * alpha
77
+
78
+ return result