deepfacelab中文网

 找回密码
 立即注册(仅限QQ邮箱)
查看: 69|回复: 15

自动画遮罩功能

[复制链接]

33

主题

223

帖子

1694

积分

初级丹圣

Rank: 8Rank: 8

积分
1694
 楼主| 发表于 昨天 21:06 | 显示全部楼层 |阅读模式
星级打分
  • 1
  • 2
  • 3
  • 4
  • 5
平均分:NAN  参与人数:0  我的评分:未评
本帖最后由 day270010678 于 2026-7-15 23:25 编辑

我刚才小幅修改了下源码,添加了个自动画遮罩的工具

屏幕截图 2026-07-15 205104.png
屏幕截图 2026-07-15 205436.png
屏幕截图 2026-07-15 205611.png
屏幕截图 2026-07-15 205648.png


原理很简单,就是加入Face Parsing人脸解析,然后把人脸直接解析出来,然后根据需求自己小幅修改,我主要是刚才画遮罩太累,就自己加了这个小功能,需要的自己修改。本质就是ONNX 人脸解析模型 + onnxruntime

安装onnxruntime库,pip安装就行了,另外下载模型,去https://hf-mirror.com/jonathandinu/face-parsing/tree/main/onnx里面下载这两个模型放到XSegEditor\models目录里面就行了

修改的文件就是XSegEditor/OcclusionDetector.py这是我自己建的ONNX 人脸解析检测器,子进程运行

  1. import multiprocessing
  2. import numpy as np
  3. import cv2
  4. from pathlib import Path


  5. class OcclusionDetector:

  6.     CMD_LOAD = 1
  7.     CMD_DETECT = 2
  8.     CMD_SHUTDOWN = 3
  9.     RSP_LOADED = 10
  10.     RSP_RESULT = 11
  11.     RSP_ERROR = 12

  12.     INCLUDE_LABELS = {1, 2, 4, 5, 6, 7, 11, 12}
  13.     EXCLUDE_LABELS = {3, 8, 9, 10, 13, 14, 15, 16, 17, 18}
  14.     HEAD_LABELS = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}

  15.     MODEL_INPUT_SIZE = 512

  16.     def __init__(self, model_path=None):
  17.         self.model_path = model_path or str(
  18.             Path(__file__).parent / 'models' / 'model_quantized.onnx')
  19.         self._req_q = multiprocessing.Queue()
  20.         self._rsp_q = multiprocessing.Queue()
  21.         self._proc = None

  22.     def start(self):
  23.         if self._proc is not None and self._proc.is_alive():
  24.             return
  25.         self._proc = multiprocessing.Process(
  26.             target=self._subprocess_run,
  27.             args=(self._req_q, self._rsp_q, self.model_path),
  28.             daemon=True,
  29.         )
  30.         self._proc.start()
  31.         rsp = self._rsp_q.get(timeout=30)
  32.         if rsp[0] == self.RSP_ERROR:
  33.             raise RuntimeError(f"OcclusionDetector load failed: {rsp[1]}")

  34.     def detect(self, img, landmarks=None):
  35.         if self._proc is None or not self._proc.is_alive():
  36.             self.start()
  37.         self._req_q.put((self.CMD_DETECT, img))
  38.         rsp = self._rsp_q.get(timeout=60)
  39.         if rsp[0] == self.RSP_ERROR:
  40.             raise RuntimeError(f"OcclusionDetector detect failed: {rsp[1]}")
  41.         return rsp[1]

  42.     def shutdown(self):
  43.         if self._proc is not None and self._proc.is_alive():
  44.             self._req_q.put((self.CMD_SHUTDOWN,))
  45.             self._proc.join(timeout=5)
  46.             if self._proc.is_alive():
  47.                 self._proc.terminate()
  48.             self._proc = None

  49.     @staticmethod
  50.     def _subprocess_run(req_q, rsp_q, model_path):
  51.         try:
  52.             import onnxruntime as ort
  53.             sess = ort.InferenceSession(model_path, providers=['CPUExecutionProvider'])
  54.             rsp_q.put((OcclusionDetector.RSP_LOADED,))

  55.             while True:
  56.                 cmd = req_q.get()
  57.                 if cmd[0] == OcclusionDetector.CMD_SHUTDOWN:
  58.                     break
  59.                 elif cmd[0] == OcclusionDetector.CMD_DETECT:
  60.                     try:
  61.                         img = cmd[1]
  62.                         result = OcclusionDetector._run_detection(sess, img)
  63.                         rsp_q.put((OcclusionDetector.RSP_RESULT, result))
  64.                     except Exception as e:
  65.                         rsp_q.put((OcclusionDetector.RSP_ERROR, str(e)))
  66.         except Exception as e:
  67.             rsp_q.put((OcclusionDetector.RSP_ERROR, str(e)))

  68.     @staticmethod
  69.     def _run_detection(sess, img):
  70.         h, w = img.shape[:2]
  71.         input_img = OcclusionDetector._preprocess(img)
  72.         logits = sess.run(None, {'pixel_values': input_img})[0]
  73.         seg_map = OcclusionDetector._logits_to_segmap(logits, h, w)
  74.         return OcclusionDetector._seg_map_to_polys(seg_map, h, w)

  75.     @staticmethod
  76.     def _preprocess(img):
  77.         blob = cv2.dnn.blobFromImage(img, 1.0 / 255.0,
  78.                                       (OcclusionDetector.MODEL_INPUT_SIZE,
  79.                                        OcclusionDetector.MODEL_INPUT_SIZE),
  80.                                       swapRB=True, crop=False)
  81.         return blob.astype(np.float32)

  82.     @staticmethod
  83.     def _logits_to_segmap(logits, h, w):
  84.         seg = logits[0]
  85.         seg = np.argmax(seg, axis=0)
  86.         seg = seg.astype(np.uint8)
  87.         seg = cv2.resize(seg, (w, h), interpolation=cv2.INTER_NEAREST)
  88.         return seg

  89.     @staticmethod
  90.     def _seg_map_to_polys(seg_map, h, w):
  91.         head_mask = np.zeros((h, w), dtype=np.uint8)
  92.         for label in OcclusionDetector.HEAD_LABELS:
  93.             head_mask[seg_map == label] = 255

  94.         include_mask = np.zeros((h, w), dtype=np.uint8)
  95.         for label in OcclusionDetector.INCLUDE_LABELS:
  96.             include_mask[seg_map == label] = 255

  97.         kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
  98.         include_mask = cv2.morphologyEx(include_mask, cv2.MORPH_CLOSE, kernel, iterations=1)
  99.         include_mask = cv2.morphologyEx(include_mask, cv2.MORPH_OPEN, kernel, iterations=1)

  100.         exclude_polys = []
  101.         for label in OcclusionDetector.EXCLUDE_LABELS:
  102.             label_mask = (seg_map == label).astype(np.uint8) * 255
  103.             overlap = cv2.bitwise_and(label_mask, head_mask)
  104.             if np.sum(overlap > 0) == 0:
  105.                 continue
  106.             overlap = cv2.morphologyEx(overlap, cv2.MORPH_CLOSE, kernel, iterations=1)
  107.             overlap = cv2.morphologyEx(overlap, cv2.MORPH_OPEN, kernel, iterations=1)
  108.             exclude_polys.extend(
  109.                 OcclusionDetector.mask_to_polys(overlap, min_area=80, epsilon_factor=0.003))

  110.         return {
  111.             'include': OcclusionDetector.mask_to_polys(include_mask, min_area=300, epsilon_factor=0.002),
  112.             'exclude': exclude_polys,
  113.         }

  114.     @staticmethod
  115.     def mask_to_polys(mask, min_area=100, epsilon_factor=0.005):
  116.         contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  117.         polys = []
  118.         for contour in contours:
  119.             if cv2.contourArea(contour) < min_area:
  120.                 continue
  121.             epsilon = epsilon_factor * cv2.arcLength(contour, True)
  122.             approx = cv2.approxPolyDP(contour, epsilon, True)
  123.             pts = approx.reshape(-1, 2).astype(np.float32)
  124.             if len(pts) >= 3:
  125.                 polys.append(pts)
  126.         return polys
复制代码


XSegEditor/XSegEditor.py
  1. import json
  2. import multiprocessing
  3. import os
  4. import pickle
  5. import sys
  6. import tempfile
  7. import time
  8. import traceback
  9. from enum import IntEnum
  10. from types import SimpleNamespace as sn

  11. import cv2
  12. import numpy as np
  13. import numpy.linalg as npla
  14. from PyQt5.QtCore import *
  15. from PyQt5.QtGui import *
  16. from PyQt5.QtWidgets import *

  17. from core import imagelib, pathex
  18. from core.cv2ex import *
  19. from core.imagelib import SegIEPoly, SegIEPolys, SegIEPolyType, sd
  20. from core.qtex import *
  21. from DFLIMG import *
  22. from localization import StringsDB, system_language
  23. from samplelib import PackedFaceset

  24. from .QCursorDB import QCursorDB
  25. from .QIconDB import QIconDB
  26. from .QStringDB import QStringDB
  27. from .QImageDB import QImageDB
  28. from .OcclusionDetector import OcclusionDetector

  29. class OpMode(IntEnum):
  30.     NONE = 0
  31.     DRAW_PTS = 1
  32.     EDIT_PTS = 2
  33.     VIEW_BAKED = 3
  34.     VIEW_XSEG_MASK = 4

  35. class PTEditMode(IntEnum):
  36.     MOVE = 0
  37.     ADD_DEL = 1

  38. class DragType(IntEnum):
  39.     NONE = 0
  40.     IMAGE_LOOK = 1
  41.     POLY_PT = 2

  42. class ViewLock(IntEnum):
  43.     NONE = 0
  44.     CENTER = 1

  45. class QUIConfig():
  46.     @staticmethod
  47.     def initialize(icon_size = 48, icon_spacer_size=16, preview_bar_icon_size=64):
  48.         QUIConfig.icon_q_size = QSize(icon_size, icon_size)
  49.         QUIConfig.icon_spacer_q_size = QSize(icon_spacer_size, icon_spacer_size)
  50.         QUIConfig.preview_bar_icon_q_size = QSize(preview_bar_icon_size, preview_bar_icon_size)

  51. class ImagePreviewSequenceBar(QFrame):
  52.     def __init__(self, preview_images_count, icon_size):
  53.         super().__init__()
  54.         self.preview_images_count = preview_images_count = max(1, preview_images_count + (preview_images_count % 2 -1) )

  55.         self.icon_size = icon_size

  56.         black_q_img = QImage(np.zeros( (icon_size,icon_size,3) ).data, icon_size, icon_size, 3*icon_size, QImage.Format_RGB888)
  57.         self.black_q_pixmap = QPixmap.fromImage(black_q_img)

  58.         self.image_containers = [ QLabel() for i in range(preview_images_count)]

  59.         main_frame_l_cont_hl = QGridLayout()
  60.         main_frame_l_cont_hl.setContentsMargins(0,0,0,0)
  61.         #main_frame_l_cont_hl.setSpacing(0)



  62.         for i in range(len(self.image_containers)):
  63.             q_label = self.image_containers[i]
  64.             q_label.setScaledContents(True)
  65.             if i == preview_images_count//2:
  66.                 q_label.setMinimumSize(icon_size+16, icon_size+16 )
  67.                 q_label.setMaximumSize(icon_size+16, icon_size+16 )
  68.             else:
  69.                 q_label.setMinimumSize(icon_size, icon_size )
  70.                 q_label.setMaximumSize(icon_size, icon_size )
  71.                 opacity_effect = QGraphicsOpacityEffect()
  72.                 opacity_effect.setOpacity(0.5)
  73.                 q_label.setGraphicsEffect(opacity_effect)

  74.             q_label.setSizePolicy (QSizePolicy.Fixed, QSizePolicy.Fixed)

  75.             main_frame_l_cont_hl.addWidget (q_label, 0, i)

  76.         self.setLayout(main_frame_l_cont_hl)

  77.         self.prev_img_conts = self.image_containers[(preview_images_count//2) -1::-1]
  78.         self.next_img_conts = self.image_containers[preview_images_count//2:]

  79.         self.update_images()

  80.     def get_preview_images_count(self):
  81.         return self.preview_images_count

  82.     def update_images(self, prev_imgs=None, next_imgs=None):
  83.         # Fix arrays
  84.         if prev_imgs is None:
  85.             prev_imgs = []
  86.         prev_img_conts_len = len(self.prev_img_conts)
  87.         prev_q_imgs_len = len(prev_imgs)
  88.         if prev_q_imgs_len < prev_img_conts_len:
  89.             for i in range ( prev_img_conts_len - prev_q_imgs_len ):
  90.                 prev_imgs.append(None)
  91.         elif prev_q_imgs_len > prev_img_conts_len:
  92.             prev_imgs = prev_imgs[:prev_img_conts_len]

  93.         if next_imgs is None:
  94.             next_imgs = []
  95.         next_img_conts_len = len(self.next_img_conts)
  96.         next_q_imgs_len = len(next_imgs)
  97.         if next_q_imgs_len < next_img_conts_len:
  98.             for i in range ( next_img_conts_len - next_q_imgs_len ):
  99.                 next_imgs.append(None)
  100.         elif next_q_imgs_len > next_img_conts_len:
  101.             next_imgs = next_imgs[:next_img_conts_len]

  102.         for i,img in enumerate(prev_imgs):
  103.             self.prev_img_conts[i].setPixmap( QPixmap.fromImage( QImage_from_np(img) ) if img is not None else self.black_q_pixmap )

  104.         for i,img in enumerate(next_imgs):
  105.             self.next_img_conts[i].setPixmap( QPixmap.fromImage( QImage_from_np(img) ) if img is not None else self.black_q_pixmap )

  106. class ColorScheme():
  107.     def __init__(self, unselected_color, selected_color, outline_color, outline_width, pt_outline_color, cross_cursor):
  108.         self.poly_unselected_brush = QBrush(unselected_color)
  109.         self.poly_selected_brush = QBrush(selected_color)

  110.         self.poly_outline_solid_pen = QPen(outline_color, outline_width, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
  111.         self.poly_outline_dot_pen = QPen(outline_color, outline_width, Qt.DotLine, Qt.RoundCap, Qt.RoundJoin)

  112.         self.pt_outline_pen = QPen(pt_outline_color)
  113.         self.cross_cursor = cross_cursor

  114. class CanvasConfig():

  115.     def __init__(self,
  116.                  pt_radius=4,
  117.                  pt_select_radius=8,
  118.                  color_schemes=None,
  119.                  **kwargs):
  120.         self.pt_radius = pt_radius
  121.         self.pt_select_radius = pt_select_radius

  122.         if color_schemes is None:
  123.             color_schemes = [
  124.                     ColorScheme( QColor(192,0,0,alpha=0), QColor(192,0,0,alpha=72), QColor(192,0,0), 2, QColor(255,255,255), QCursorDB.cross_red ),
  125.                     ColorScheme( QColor(0,192,0,alpha=0), QColor(0,192,0,alpha=72), QColor(0,192,0), 2, QColor(255,255,255), QCursorDB.cross_green ),
  126.                     ColorScheme( QColor(0,0,192,alpha=0), QColor(0,0,192,alpha=72), QColor(0,0,192), 2, QColor(255,255,255), QCursorDB.cross_blue ),
  127.                     ]
  128.         self.color_schemes = color_schemes

  129. class QCanvasControlsLeftBar(QFrame):

  130.     def __init__(self):
  131.         super().__init__()
  132.         #==============================================
  133.         btn_poly_type_include = QToolButton()
  134.         self.btn_poly_type_include_act = QActionEx( QIconDB.poly_type_include, QStringDB.btn_poly_type_include_tip, shortcut='Q', shortcut_in_tooltip=True, is_checkable=True)
  135.         btn_poly_type_include.setDefaultAction(self.btn_poly_type_include_act)
  136.         btn_poly_type_include.setIconSize(QUIConfig.icon_q_size)

  137.         btn_poly_type_exclude = QToolButton()
  138.         self.btn_poly_type_exclude_act = QActionEx( QIconDB.poly_type_exclude, QStringDB.btn_poly_type_exclude_tip, shortcut='W', shortcut_in_tooltip=True, is_checkable=True)
  139.         btn_poly_type_exclude.setDefaultAction(self.btn_poly_type_exclude_act)
  140.         btn_poly_type_exclude.setIconSize(QUIConfig.icon_q_size)

  141.         self.btn_poly_type_act_grp = QActionGroup (self)
  142.         self.btn_poly_type_act_grp.addAction(self.btn_poly_type_include_act)
  143.         self.btn_poly_type_act_grp.addAction(self.btn_poly_type_exclude_act)
  144.         self.btn_poly_type_act_grp.setExclusive(True)
  145.         #==============================================
  146.         btn_undo_pt = QToolButton()
  147.         self.btn_undo_pt_act = QActionEx( QIconDB.undo_pt, QStringDB.btn_undo_pt_tip, shortcut='Ctrl+Z', shortcut_in_tooltip=True, is_auto_repeat=True)
  148.         btn_undo_pt.setDefaultAction(self.btn_undo_pt_act)
  149.         btn_undo_pt.setIconSize(QUIConfig.icon_q_size)

  150.         btn_redo_pt = QToolButton()
  151.         self.btn_redo_pt_act = QActionEx( QIconDB.redo_pt, QStringDB.btn_redo_pt_tip, shortcut='Ctrl+Shift+Z',  shortcut_in_tooltip=True, is_auto_repeat=True)
  152.         btn_redo_pt.setDefaultAction(self.btn_redo_pt_act)
  153.         btn_redo_pt.setIconSize(QUIConfig.icon_q_size)

  154.         btn_delete_poly = QToolButton()
  155.         self.btn_delete_poly_act = QActionEx( QIconDB.delete_poly, QStringDB.btn_delete_poly_tip, shortcut='Delete', shortcut_in_tooltip=True)
  156.         btn_delete_poly.setDefaultAction(self.btn_delete_poly_act)
  157.         btn_delete_poly.setIconSize(QUIConfig.icon_q_size)
  158.         #==============================================
  159.         btn_pt_edit_mode = QToolButton()
  160.         self.btn_pt_edit_mode_act = QActionEx( QIconDB.pt_edit_mode, QStringDB.btn_pt_edit_mode_tip, shortcut_in_tooltip=True, is_checkable=True)
  161.         btn_pt_edit_mode.setDefaultAction(self.btn_pt_edit_mode_act)
  162.         btn_pt_edit_mode.setIconSize(QUIConfig.icon_q_size)
  163.         #==============================================

  164.         controls_bar_frame2_l = QVBoxLayout()
  165.         controls_bar_frame2_l.addWidget ( btn_poly_type_include )
  166.         controls_bar_frame2_l.addWidget ( btn_poly_type_exclude )
  167.         controls_bar_frame2 = QFrame()
  168.         controls_bar_frame2.setFrameShape(QFrame.StyledPanel)
  169.         controls_bar_frame2.setSizePolicy (QSizePolicy.Fixed, QSizePolicy.Fixed)
  170.         controls_bar_frame2.setLayout(controls_bar_frame2_l)

  171.         controls_bar_frame3_l = QVBoxLayout()
  172.         controls_bar_frame3_l.addWidget ( btn_undo_pt )
  173.         controls_bar_frame3_l.addWidget ( btn_redo_pt )
  174.         controls_bar_frame3_l.addWidget ( btn_delete_poly )
  175.         controls_bar_frame3 = QFrame()
  176.         controls_bar_frame3.setFrameShape(QFrame.StyledPanel)
  177.         controls_bar_frame3.setSizePolicy (QSizePolicy.Fixed, QSizePolicy.Fixed)
  178.         controls_bar_frame3.setLayout(controls_bar_frame3_l)

  179.         controls_bar_frame4_l = QVBoxLayout()
  180.         controls_bar_frame4_l.addWidget ( btn_pt_edit_mode )
  181.         controls_bar_frame4 = QFrame()
  182.         controls_bar_frame4.setFrameShape(QFrame.StyledPanel)
  183.         controls_bar_frame4.setSizePolicy (QSizePolicy.Fixed, QSizePolicy.Fixed)
  184.         controls_bar_frame4.setLayout(controls_bar_frame4_l)
  185.         #==============================================
  186.         btn_auto_detect = QToolButton()
  187.         self.btn_auto_detect_act = QActionEx( QIconDB.auto_detect, QStringDB.btn_auto_detect_tip, shortcut='F', shortcut_in_tooltip=True)
  188.         btn_auto_detect.setDefaultAction(self.btn_auto_detect_act)
  189.         btn_auto_detect.setIconSize(QUIConfig.icon_q_size)

  190.         controls_bar_frame5_l = QVBoxLayout()
  191.         controls_bar_frame5_l.addWidget ( btn_auto_detect )
  192.         controls_bar_frame5 = QFrame()
  193.         controls_bar_frame5.setFrameShape(QFrame.StyledPanel)
  194.         controls_bar_frame5.setSizePolicy (QSizePolicy.Fixed, QSizePolicy.Fixed)
  195.         controls_bar_frame5.setLayout(controls_bar_frame5_l)

  196.         controls_bar_l = QVBoxLayout()
  197.         controls_bar_l.setContentsMargins(0,0,0,0)
  198.         controls_bar_l.addWidget(controls_bar_frame2)
  199.         controls_bar_l.addWidget(controls_bar_frame3)
  200.         controls_bar_l.addWidget(controls_bar_frame4)
  201.         controls_bar_l.addWidget(controls_bar_frame5)

  202.         self.setSizePolicy ( QSizePolicy.Fixed, QSizePolicy.Expanding )
  203.         self.setLayout(controls_bar_l)

  204. class QCanvasControlsRightBar(QFrame):

  205.     def __init__(self):
  206.         super().__init__()
  207.         #==============================================
  208.         btn_poly_color_red = QToolButton()
  209.         self.btn_poly_color_red_act = QActionEx( QIconDB.poly_color_red, QStringDB.btn_poly_color_red_tip, shortcut='1', shortcut_in_tooltip=True, is_checkable=True)
  210.         btn_poly_color_red.setDefaultAction(self.btn_poly_color_red_act)
  211.         btn_poly_color_red.setIconSize(QUIConfig.icon_q_size)

  212.         btn_poly_color_green = QToolButton()
  213.         self.btn_poly_color_green_act = QActionEx( QIconDB.poly_color_green, QStringDB.btn_poly_color_green_tip, shortcut='2', shortcut_in_tooltip=True, is_checkable=True)
  214.         btn_poly_color_green.setDefaultAction(self.btn_poly_color_green_act)
  215.         btn_poly_color_green.setIconSize(QUIConfig.icon_q_size)

  216.         btn_poly_color_blue = QToolButton()
  217.         self.btn_poly_color_blue_act = QActionEx( QIconDB.poly_color_blue, QStringDB.btn_poly_color_blue_tip, shortcut='3', shortcut_in_tooltip=True, is_checkable=True)
  218.         btn_poly_color_blue.setDefaultAction(self.btn_poly_color_blue_act)
  219.         btn_poly_color_blue.setIconSize(QUIConfig.icon_q_size)

  220.         btn_view_baked_mask = QToolButton()
  221.         self.btn_view_baked_mask_act = QActionEx( QIconDB.view_baked, QStringDB.btn_view_baked_mask_tip, shortcut='4', shortcut_in_tooltip=True, is_checkable=True)
  222.         btn_view_baked_mask.setDefaultAction(self.btn_view_baked_mask_act)
  223.         btn_view_baked_mask.setIconSize(QUIConfig.icon_q_size)

  224.         btn_view_xseg_mask = QToolButton()
  225.         self.btn_view_xseg_mask_act = QActionEx( QIconDB.view_xseg, QStringDB.btn_view_xseg_mask_tip, shortcut='5', shortcut_in_tooltip=True, is_checkable=True)
  226.         btn_view_xseg_mask.setDefaultAction(self.btn_view_xseg_mask_act)
  227.         btn_view_xseg_mask.setIconSize(QUIConfig.icon_q_size)

  228.         btn_view_xseg_overlay_mask = QToolButton()
  229.         self.btn_view_xseg_overlay_mask_act = QActionEx( QIconDB.view_xseg_overlay, QStringDB.btn_view_xseg_overlay_mask_tip, shortcut='`', shortcut_in_tooltip=True, is_checkable=True)
  230.         btn_view_xseg_overlay_mask.setDefaultAction(self.btn_view_xseg_overlay_mask_act)
  231.         btn_view_xseg_overlay_mask.setIconSize(QUIConfig.icon_q_size)

  232.         self.btn_poly_color_act_grp = QActionGroup (self)
  233.         self.btn_poly_color_act_grp.addAction(self.btn_poly_color_red_act)
  234.         self.btn_poly_color_act_grp.addAction(self.btn_poly_color_green_act)
  235.         self.btn_poly_color_act_grp.addAction(self.btn_poly_color_blue_act)
  236.         self.btn_poly_color_act_grp.addAction(self.btn_view_baked_mask_act)
  237.         self.btn_poly_color_act_grp.addAction(self.btn_view_xseg_mask_act)
  238.         self.btn_poly_color_act_grp.setExclusive(True)
  239.         #==============================================
  240.         btn_view_lock_center = QToolButton()
  241.         self.btn_view_lock_center_act = QActionEx( QIconDB.view_lock_center, QStringDB.btn_view_lock_center_tip, shortcut_in_tooltip=True, is_checkable=True)
  242.         btn_view_lock_center.setDefaultAction(self.btn_view_lock_center_act)
  243.         btn_view_lock_center.setIconSize(QUIConfig.icon_q_size)

  244.         controls_bar_frame2_l = QVBoxLayout()
  245.         controls_bar_frame2_l.addWidget ( btn_view_xseg_overlay_mask )
  246.         controls_bar_frame2 = QFrame()
  247.         controls_bar_frame2.setFrameShape(QFrame.StyledPanel)
  248.         controls_bar_frame2.setSizePolicy (QSizePolicy.Fixed, QSizePolicy.Fixed)
  249.         controls_bar_frame2.setLayout(controls_bar_frame2_l)
  250.         
  251.         controls_bar_frame1_l = QVBoxLayout()
  252.         controls_bar_frame1_l.addWidget ( btn_poly_color_red )
  253.         controls_bar_frame1_l.addWidget ( btn_poly_color_green )
  254.         controls_bar_frame1_l.addWidget ( btn_poly_color_blue )
  255.         controls_bar_frame1_l.addWidget ( btn_view_baked_mask )
  256.         controls_bar_frame1_l.addWidget ( btn_view_xseg_mask )
  257.         controls_bar_frame1 = QFrame()
  258.         controls_bar_frame1.setFrameShape(QFrame.StyledPanel)
  259.         controls_bar_frame1.setSizePolicy (QSizePolicy.Fixed, QSizePolicy.Fixed)
  260.         controls_bar_frame1.setLayout(controls_bar_frame1_l)
  261.         
  262.         controls_bar_frame3_l = QVBoxLayout()
  263.         controls_bar_frame3_l.addWidget ( btn_view_lock_center )
  264.         controls_bar_frame3 = QFrame()
  265.         controls_bar_frame3.setFrameShape(QFrame.StyledPanel)
  266.         controls_bar_frame3.setSizePolicy (QSizePolicy.Fixed, QSizePolicy.Fixed)
  267.         controls_bar_frame3.setLayout(controls_bar_frame3_l)

  268.         controls_bar_l = QVBoxLayout()
  269.         controls_bar_l.setContentsMargins(0,0,0,0)
  270.         controls_bar_l.addWidget(controls_bar_frame2)
  271.         controls_bar_l.addWidget(controls_bar_frame1)
  272.         controls_bar_l.addWidget(controls_bar_frame3)
  273.         
  274.         self.setSizePolicy ( QSizePolicy.Fixed, QSizePolicy.Expanding )
  275.         self.setLayout(controls_bar_l)

  276. class QCanvasOperator(QWidget):
  277.     def __init__(self, cbar):
  278.         super().__init__()
  279.         self.cbar = cbar

  280.         self.set_cbar_disabled()

  281.         self.cbar.btn_poly_color_red_act.triggered.connect ( lambda : self.set_color_scheme_id(0) )
  282.         self.cbar.btn_poly_color_green_act.triggered.connect ( lambda : self.set_color_scheme_id(1) )
  283.         self.cbar.btn_poly_color_blue_act.triggered.connect ( lambda : self.set_color_scheme_id(2) )
  284.         self.cbar.btn_view_baked_mask_act.triggered.connect ( lambda : self.set_op_mode(OpMode.VIEW_BAKED) )
  285.         self.cbar.btn_view_xseg_mask_act.triggered.connect ( lambda : self.set_op_mode(OpMode.VIEW_XSEG_MASK) )

  286.         self.cbar.btn_view_xseg_overlay_mask_act.toggled.connect ( lambda is_checked: self.update() )

  287.         self.cbar.btn_poly_type_include_act.triggered.connect ( lambda : self.set_poly_include_type(SegIEPolyType.INCLUDE) )
  288.         self.cbar.btn_poly_type_exclude_act.triggered.connect ( lambda : self.set_poly_include_type(SegIEPolyType.EXCLUDE) )

  289.         self.cbar.btn_undo_pt_act.triggered.connect ( lambda : self.action_undo_pt() )
  290.         self.cbar.btn_redo_pt_act.triggered.connect ( lambda : self.action_redo_pt() )

  291.         self.cbar.btn_delete_poly_act.triggered.connect ( lambda : self.action_delete_poly() )

  292.         self.cbar.btn_auto_detect_act.triggered.connect ( lambda : self.action_auto_detect() )

  293.         self.cbar.btn_pt_edit_mode_act.toggled.connect ( lambda is_checked: self.set_pt_edit_mode( PTEditMode.ADD_DEL if is_checked else PTEditMode.MOVE ) )
  294.         self.cbar.btn_view_lock_center_act.toggled.connect ( lambda is_checked: self.set_view_lock( ViewLock.CENTER if is_checked else ViewLock.NONE ) )

  295.         self.mouse_in_widget = False

  296.         QXMainWindow.inst.add_keyPressEvent_listener ( self.on_keyPressEvent )
  297.         QXMainWindow.inst.add_keyReleaseEvent_listener ( self.on_keyReleaseEvent )

  298.         self.qp = QPainter()
  299.         self.initialized = False
  300.         self.last_state = None

  301.     def initialize(self, img, img_look_pt=None, view_scale=None, ie_polys=None, xseg_mask=None, canvas_config=None, landmarks=None ):
  302.         self.q_img_copy = img.copy()
  303.         self.landmarks = landmarks
  304.         q_img = self.q_img = QImage_from_np(img)
  305.         self.img_pixmap = QPixmap.fromImage(q_img)

  306.         self.xseg_mask_pixmap = None
  307.         self.xseg_overlay_mask_pixmap = None
  308.         if xseg_mask is not None:
  309.             h,w,c = img.shape
  310.             xseg_mask = cv2.resize(xseg_mask, (w,h), interpolation=cv2.INTER_CUBIC)
  311.             xseg_mask = imagelib.normalize_channels(xseg_mask, 1)
  312.             xseg_img = img.astype(np.float32)/255.0
  313.             xseg_overlay_mask = xseg_img*(1-xseg_mask)*0.5 + xseg_img*xseg_mask
  314.             xseg_overlay_mask = np.clip(xseg_overlay_mask*255, 0, 255).astype(np.uint8)
  315.             xseg_mask = np.clip(xseg_mask*255, 0, 255).astype(np.uint8)
  316.             self.xseg_mask_pixmap = QPixmap.fromImage(QImage_from_np(xseg_mask))
  317.             self.xseg_overlay_mask_pixmap = QPixmap.fromImage(QImage_from_np(xseg_overlay_mask))

  318.         self.img_size = QSize_to_np (self.img_pixmap.size())

  319.         self.img_look_pt = img_look_pt
  320.         self.view_scale = view_scale

  321.         if ie_polys is None:
  322.             ie_polys = SegIEPolys()
  323.         self.ie_polys = ie_polys

  324.         if canvas_config is None:
  325.             canvas_config = CanvasConfig()
  326.         self.canvas_config = canvas_config

  327.         # UI init
  328.         self.set_cbar_disabled()
  329.         self.cbar.btn_poly_color_act_grp.setDisabled(False)
  330.         self.cbar.btn_view_xseg_overlay_mask_act.setDisabled(False)
  331.         self.cbar.btn_poly_type_act_grp.setDisabled(False)

  332.         # Initial vars
  333.         self.current_cursor = None
  334.         self.mouse_hull_poly = None
  335.         self.mouse_wire_poly = None
  336.         self.drag_type = DragType.NONE
  337.         self.mouse_cli_pt = np.zeros((2,), np.float32 )

  338.         # Initial state
  339.         self.set_op_mode(OpMode.NONE)
  340.         self.set_color_scheme_id(1)
  341.         self.set_poly_include_type(SegIEPolyType.INCLUDE)
  342.         self.set_pt_edit_mode(PTEditMode.MOVE)
  343.         self.set_view_lock(ViewLock.NONE)

  344.         # Apply last state
  345.         if self.last_state is not None:
  346.             self.set_color_scheme_id(self.last_state.color_scheme_id)
  347.             if self.last_state.op_mode is not None:
  348.                 self.set_op_mode(self.last_state.op_mode)

  349.         self.initialized = True

  350.         self.setMouseTracking(True)
  351.         self.update_cursor()
  352.         self.update()


  353.     def finalize(self):
  354.         if self.initialized:
  355.             if self.op_mode == OpMode.DRAW_PTS:
  356.                 self.set_op_mode(OpMode.EDIT_PTS)

  357.             self.last_state = sn(op_mode = self.op_mode if self.op_mode in [OpMode.VIEW_BAKED, OpMode.VIEW_XSEG_MASK] else None,
  358.                                  color_scheme_id = self.color_scheme_id)

  359.             self.img_pixmap = None
  360.             self.update_cursor(is_finalize=True)
  361.             self.setMouseTracking(False)
  362.             self.setFocusPolicy(Qt.NoFocus)
  363.             self.set_cbar_disabled()
  364.             self.initialized = False
  365.             self.update()

  366.     # ====================================================================================
  367.     # ====================================================================================
  368.     # ====================================== GETTERS =====================================
  369.     # ====================================================================================
  370.     # ====================================================================================
  371.     def is_initialized(self):
  372.         return self.initialized

  373.     def get_ie_polys(self):
  374.         return self.ie_polys

  375.     def get_cli_center_pt(self):
  376.         return np.round(QSize_to_np(self.size())/2.0)

  377.     def get_img_look_pt(self):
  378.         img_look_pt = self.img_look_pt
  379.         if img_look_pt is None:
  380.             img_look_pt = self.img_size / 2
  381.         return img_look_pt

  382.     def get_view_scale(self):
  383.         view_scale = self.view_scale
  384.         if view_scale is None:
  385.             # Calc as scale to fit
  386.             min_cli_size = np.min(QSize_to_np(self.size()))
  387.             max_img_size = np.max(self.img_size)
  388.             view_scale =  min_cli_size / max_img_size

  389.         return view_scale

  390.     def get_current_color_scheme(self):
  391.         return self.canvas_config.color_schemes[self.color_scheme_id]

  392.     def get_poly_pt_id_under_pt(self, poly, cli_pt):
  393.         w = np.argwhere ( npla.norm ( cli_pt - self.img_to_cli_pt( poly.get_pts() ), axis=1 )  <= self.canvas_config.pt_select_radius )
  394.         return None if len(w) == 0 else w[-1][0]

  395.     def get_poly_edge_id_pt_under_pt(self, poly, cli_pt):
  396.         cli_pts = self.img_to_cli_pt(poly.get_pts())
  397.         if len(cli_pts) >= 3:
  398.             edge_dists, projs = sd.dist_to_edges(cli_pts, cli_pt, is_closed=True)
  399.             edge_id = np.argmin(edge_dists)
  400.             dist = edge_dists[edge_id]
  401.             pt = projs[edge_id]
  402.             if dist <= self.canvas_config.pt_select_radius:
  403.                 return edge_id, pt
  404.         return None, None

  405.     def get_poly_by_pt_near_wire(self, cli_pt):
  406.         pt_select_radius = self.canvas_config.pt_select_radius

  407.         for poly in reversed(self.ie_polys.get_polys()):
  408.             pts = poly.get_pts()
  409.             if len(pts) >= 3:
  410.                 cli_pts = self.img_to_cli_pt(pts)

  411.                 edge_dists, _ = sd.dist_to_edges(cli_pts, cli_pt, is_closed=True)

  412.                 if np.min(edge_dists) <= pt_select_radius or \
  413.                    any( npla.norm ( cli_pt - cli_pts, axis=1 ) <= pt_select_radius ):
  414.                     return poly
  415.         return None

  416.     def get_poly_by_pt_in_hull(self, cli_pos):
  417.         img_pos = self.cli_to_img_pt(cli_pos)

  418.         for poly in reversed(self.ie_polys.get_polys()):
  419.             pts = poly.get_pts()
  420.             if len(pts) >= 3:
  421.                 if cv2.pointPolygonTest( pts, tuple(img_pos), False) >= 0:
  422.                     return poly

  423.         return None

  424.     def img_to_cli_pt(self, p):
  425.         return (p - self.get_img_look_pt()) * self.get_view_scale() + self.get_cli_center_pt()# QSize_to_np(self.size())/2.0

  426.     def cli_to_img_pt(self, p):
  427.         return (p - self.get_cli_center_pt() ) / self.get_view_scale() + self.get_img_look_pt()

  428.     def img_to_cli_rect(self, rect):
  429.         tl = QPoint_to_np(rect.topLeft())
  430.         xy = self.img_to_cli_pt(tl)
  431.         xy2 = self.img_to_cli_pt(tl + QSize_to_np(rect.size()) ) - xy
  432.         return QRect ( *xy.astype(np.int64), *xy2.astype(np.int64) )

  433.     # ====================================================================================
  434.     # ====================================================================================
  435.     # ====================================== SETTERS =====================================
  436.     # ====================================================================================
  437.     # ====================================================================================
  438.     def set_op_mode(self, op_mode, op_poly=None):
  439.         if not hasattr(self,'op_mode'):
  440.             self.op_mode = None
  441.             self.op_poly = None

  442.         if self.op_mode != op_mode:
  443.             # Finalize prev mode
  444.             if self.op_mode == OpMode.NONE:
  445.                 self.cbar.btn_poly_type_act_grp.setDisabled(True)
  446.             elif self.op_mode == OpMode.DRAW_PTS:
  447.                 self.cbar.btn_undo_pt_act.setDisabled(True)
  448.                 self.cbar.btn_redo_pt_act.setDisabled(True)
  449.                 self.cbar.btn_view_lock_center_act.setDisabled(True)
  450.                 # Reset view_lock when exit from DRAW_PTS
  451.                 self.set_view_lock(ViewLock.NONE)
  452.                 # Remove unfinished poly
  453.                 if self.op_poly.get_pts_count() < 3:
  454.                     self.ie_polys.remove_poly(self.op_poly)

  455.             elif self.op_mode == OpMode.EDIT_PTS:
  456.                 self.cbar.btn_pt_edit_mode_act.setDisabled(True)
  457.                 self.cbar.btn_delete_poly_act.setDisabled(True)
  458.                 # Reset pt_edit_move when exit from EDIT_PTS
  459.                 self.set_pt_edit_mode(PTEditMode.MOVE)
  460.             elif self.op_mode == OpMode.VIEW_BAKED:
  461.                 self.cbar.btn_view_baked_mask_act.setChecked(False)
  462.             elif self.op_mode == OpMode.VIEW_XSEG_MASK:
  463.                 self.cbar.btn_view_xseg_mask_act.setChecked(False)

  464.             self.op_mode = op_mode

  465.             # Initialize new mode
  466.             if op_mode == OpMode.NONE:
  467.                 self.cbar.btn_poly_type_act_grp.setDisabled(False)
  468.             elif op_mode == OpMode.DRAW_PTS:
  469.                 self.cbar.btn_undo_pt_act.setDisabled(False)
  470.                 self.cbar.btn_redo_pt_act.setDisabled(False)
  471.                 self.cbar.btn_view_lock_center_act.setDisabled(False)
  472.             elif op_mode == OpMode.EDIT_PTS:
  473.                 self.cbar.btn_pt_edit_mode_act.setDisabled(False)
  474.                 self.cbar.btn_delete_poly_act.setDisabled(False)
  475.             elif op_mode == OpMode.VIEW_BAKED:
  476.                 self.cbar.btn_view_baked_mask_act.setChecked(True )
  477.                 n = QImage_to_np ( self.q_img ).astype(np.float32) / 255.0
  478.                 h,w,c = n.shape
  479.                 mask = np.zeros( (h,w,1), dtype=np.float32 )
  480.                 self.ie_polys.overlay_mask(mask)
  481.                 n = (mask*255).astype(np.uint8)
  482.                 self.img_baked_pixmap = QPixmap.fromImage(QImage_from_np(n))
  483.             elif op_mode == OpMode.VIEW_XSEG_MASK:
  484.                 self.cbar.btn_view_xseg_mask_act.setChecked(True)

  485.             if op_mode in [OpMode.DRAW_PTS, OpMode.EDIT_PTS]:
  486.                 self.mouse_op_poly_pt_id = None
  487.                 self.mouse_op_poly_edge_id = None
  488.                 self.mouse_op_poly_edge_id_pt = None

  489.             self.op_poly = op_poly
  490.             if op_poly is not None:
  491.                 self.update_mouse_info()

  492.             self.update_cursor()
  493.             self.update()

  494.     def set_pt_edit_mode(self, pt_edit_mode):
  495.         if not hasattr(self, 'pt_edit_mode') or self.pt_edit_mode != pt_edit_mode:
  496.             self.pt_edit_mode = pt_edit_mode
  497.             self.update_cursor()
  498.             self.update()
  499.         self.cbar.btn_pt_edit_mode_act.setChecked( self.pt_edit_mode == PTEditMode.ADD_DEL )

  500.     def set_view_lock(self, view_lock):
  501.         if not hasattr(self, 'view_lock') or self.view_lock != view_lock:
  502.             if hasattr(self, 'view_lock') and self.view_lock != view_lock:
  503.                 if view_lock == ViewLock.CENTER:
  504.                     self.img_look_pt = self.mouse_img_pt
  505.                     QCursor.setPos ( self.mapToGlobal( QPoint_from_np(self.img_to_cli_pt(self.img_look_pt)) ))

  506.             self.view_lock = view_lock
  507.             self.update()
  508.         self.cbar.btn_view_lock_center_act.setChecked( self.view_lock == ViewLock.CENTER )

  509.     def set_cbar_disabled(self):
  510.         self.cbar.btn_delete_poly_act.setDisabled(True)
  511.         self.cbar.btn_undo_pt_act.setDisabled(True)
  512.         self.cbar.btn_redo_pt_act.setDisabled(True)
  513.         self.cbar.btn_pt_edit_mode_act.setDisabled(True)
  514.         self.cbar.btn_view_lock_center_act.setDisabled(True)
  515.         self.cbar.btn_poly_color_act_grp.setDisabled(True)
  516.         self.cbar.btn_view_xseg_overlay_mask_act.setDisabled(True)
  517.         self.cbar.btn_poly_type_act_grp.setDisabled(True)


  518.     def set_color_scheme_id(self, id):
  519.         if self.op_mode == OpMode.VIEW_BAKED or self.op_mode == OpMode.VIEW_XSEG_MASK:
  520.             self.set_op_mode(OpMode.NONE)

  521.         if not hasattr(self, 'color_scheme_id') or self.color_scheme_id != id:
  522.             self.color_scheme_id = id
  523.             self.update_cursor()
  524.             self.update()

  525.         if self.color_scheme_id == 0:
  526.             self.cbar.btn_poly_color_red_act.setChecked( True )
  527.         elif self.color_scheme_id == 1:
  528.             self.cbar.btn_poly_color_green_act.setChecked( True )
  529.         elif self.color_scheme_id == 2:
  530.             self.cbar.btn_poly_color_blue_act.setChecked( True )

  531.     def set_poly_include_type(self, poly_include_type):
  532.         if not hasattr(self, 'poly_include_type' ) or \
  533.            ( self.poly_include_type != poly_include_type and \
  534.              self.op_mode in [OpMode.NONE, OpMode.EDIT_PTS] ):
  535.             self.poly_include_type = poly_include_type
  536.             self.update()
  537.         self.cbar.btn_poly_type_include_act.setChecked(self.poly_include_type == SegIEPolyType.INCLUDE)
  538.         self.cbar.btn_poly_type_exclude_act.setChecked(self.poly_include_type == SegIEPolyType.EXCLUDE)

  539.     # ====================================================================================
  540.     # ====================================================================================
  541.     # ====================================== METHODS =====================================
  542.     # ====================================================================================
  543.     # ====================================================================================

  544.     def update_cursor(self, is_finalize=False):
  545.         if not self.initialized:
  546.             return

  547.         if not self.mouse_in_widget or is_finalize:
  548.             if self.current_cursor is not None:
  549.                 QApplication.restoreOverrideCursor()
  550.                 self.current_cursor = None
  551.         else:
  552.             color_cc = self.get_current_color_scheme().cross_cursor
  553.             nc = Qt.ArrowCursor

  554.             if self.drag_type == DragType.IMAGE_LOOK:
  555.                 nc = Qt.ClosedHandCursor
  556.             else:

  557.                 if self.op_mode == OpMode.NONE:
  558.                     nc = color_cc
  559.                     if self.mouse_wire_poly is not None:
  560.                         nc = Qt.PointingHandCursor

  561.                 elif self.op_mode == OpMode.DRAW_PTS:
  562.                     nc = color_cc
  563.                 elif self.op_mode == OpMode.EDIT_PTS:
  564.                     nc = Qt.ArrowCursor

  565.                     if self.mouse_op_poly_pt_id is not None:
  566.                         nc = Qt.PointingHandCursor

  567.                     if self.pt_edit_mode == PTEditMode.ADD_DEL:

  568.                         if self.mouse_op_poly_edge_id is not None and \
  569.                         self.mouse_op_poly_pt_id is None:
  570.                             nc = color_cc
  571.             if self.current_cursor != nc:
  572.                 if self.current_cursor is None:
  573.                     QApplication.setOverrideCursor(nc)
  574.                 else:
  575.                     QApplication.changeOverrideCursor(nc)
  576.                 self.current_cursor = nc

  577.     def update_mouse_info(self, mouse_cli_pt=None):
  578.         """
  579.         Update selected polys/edges/points by given mouse position
  580.         """
  581.         if mouse_cli_pt is not None:
  582.             self.mouse_cli_pt = mouse_cli_pt.astype(np.float32)

  583.         self.mouse_img_pt = self.cli_to_img_pt(self.mouse_cli_pt)

  584.         new_mouse_hull_poly = self.get_poly_by_pt_in_hull(self.mouse_cli_pt)

  585.         if self.mouse_hull_poly != new_mouse_hull_poly:
  586.             self.mouse_hull_poly = new_mouse_hull_poly
  587.             self.update_cursor()
  588.             self.update()

  589.         new_mouse_wire_poly = self.get_poly_by_pt_near_wire(self.mouse_cli_pt)

  590.         if self.mouse_wire_poly != new_mouse_wire_poly:
  591.             self.mouse_wire_poly = new_mouse_wire_poly
  592.             self.update_cursor()
  593.             self.update()

  594.         if self.op_mode in [OpMode.DRAW_PTS, OpMode.EDIT_PTS]:
  595.             new_mouse_op_poly_pt_id = self.get_poly_pt_id_under_pt (self.op_poly, self.mouse_cli_pt)
  596.             if self.mouse_op_poly_pt_id != new_mouse_op_poly_pt_id:
  597.                 self.mouse_op_poly_pt_id = new_mouse_op_poly_pt_id
  598.                 self.update_cursor()
  599.                 self.update()

  600.             new_mouse_op_poly_edge_id,\
  601.             new_mouse_op_poly_edge_id_pt = self.get_poly_edge_id_pt_under_pt (self.op_poly, self.mouse_cli_pt)
  602.             if self.mouse_op_poly_edge_id != new_mouse_op_poly_edge_id:
  603.                 self.mouse_op_poly_edge_id = new_mouse_op_poly_edge_id
  604.                 self.update_cursor()
  605.                 self.update()

  606.             if (self.mouse_op_poly_edge_id_pt.__class__ != new_mouse_op_poly_edge_id_pt.__class__) or \
  607.                (isinstance(self.mouse_op_poly_edge_id_pt, np.ndarray) and \
  608.                 all(self.mouse_op_poly_edge_id_pt != new_mouse_op_poly_edge_id_pt)):

  609.                 self.mouse_op_poly_edge_id_pt = new_mouse_op_poly_edge_id_pt
  610.                 self.update_cursor()
  611.                 self.update()


  612.     def action_undo_pt(self):
  613.         if self.drag_type == DragType.NONE:
  614.             if self.op_mode == OpMode.DRAW_PTS:
  615.                 if self.op_poly.undo() == 0:
  616.                     self.ie_polys.remove_poly (self.op_poly)
  617.                     self.set_op_mode(OpMode.NONE)
  618.                 self.update()

  619.     def action_redo_pt(self):
  620.         if self.drag_type == DragType.NONE:
  621.             if self.op_mode == OpMode.DRAW_PTS:
  622.                 self.op_poly.redo()
  623.                 self.update()

  624.     def action_delete_poly(self):
  625.         if self.op_mode == OpMode.EDIT_PTS and \
  626.             self.drag_type == DragType.NONE and \
  627.             self.pt_edit_mode == PTEditMode.MOVE:
  628.             # Delete current poly
  629.             self.ie_polys.remove_poly (self.op_poly)
  630.             self.set_op_mode(OpMode.NONE)

  631.     def action_auto_detect(self):
  632.         if not self.initialized:
  633.             return
  634.         if getattr(self, '_detect_running', False):
  635.             return
  636.         img = self.q_img_copy if hasattr(self, 'q_img_copy') else None
  637.         if img is None:
  638.             return
  639.         detector = getattr(self, '_occlusion_detector', None)
  640.         if detector is None:
  641.             return
  642.         self._detect_running = True
  643.         self.cbar.btn_auto_detect_act.setDisabled(True)

  644.         class _DetectThread(QThread):
  645.             result_ready = pyqtSignal(object)
  646.             def __init__(self, detector, img):
  647.                 super().__init__()
  648.                 self.detector = detector
  649.                 self.img = img
  650.             def run(self):
  651.                 try:
  652.                     polys_data = self.detector.detect(self.img)
  653.                     self.result_ready.emit(polys_data)
  654.                 except Exception as e:
  655.                     self.result_ready.emit(None)

  656.         def on_result(polys_data):
  657.             self._detect_running = False
  658.             self.cbar.btn_auto_detect_act.setDisabled(False)
  659.             if polys_data is not None:
  660.                 include_polys = polys_data.get('include', [])
  661.                 exclude_polys = polys_data.get('exclude', [])
  662.                 for pts in include_polys:
  663.                     poly = self.ie_polys.add_poly(SegIEPolyType.INCLUDE)
  664.                     poly.set_points(pts)
  665.                 for pts in exclude_polys:
  666.                     poly = self.ie_polys.add_poly(SegIEPolyType.EXCLUDE)
  667.                     poly.set_points(pts)
  668.                 if len(include_polys) > 0 or len(exclude_polys) > 0:
  669.                     self.set_op_mode(OpMode.NONE)
  670.                     self.update()

  671.         self._detect_thread = _DetectThread(detector, img)
  672.         self._detect_thread.result_ready.connect(on_result)
  673.         self._detect_thread.start()

  674.     # ====================================================================================
  675.     # ====================================================================================
  676.     # ================================== OVERRIDE QT METHODS =============================
  677.     # ====================================================================================
  678.     # ====================================================================================
  679.     def on_keyPressEvent(self, ev):
  680.         if not self.initialized:
  681.             return
  682.         key = ev.key()
  683.         key_mods = int(ev.modifiers())
  684.         if self.op_mode == OpMode.DRAW_PTS:
  685.             self.set_view_lock(ViewLock.CENTER if key_mods == Qt.ShiftModifier else ViewLock.NONE )
  686.         elif self.op_mode == OpMode.EDIT_PTS:
  687.             self.set_pt_edit_mode(PTEditMode.ADD_DEL if key_mods == Qt.ControlModifier else PTEditMode.MOVE )

  688.     def on_keyReleaseEvent(self, ev):
  689.         if not self.initialized:
  690.             return
  691.         key = ev.key()
  692.         key_mods = int(ev.modifiers())
  693.         if self.op_mode == OpMode.DRAW_PTS:
  694.             self.set_view_lock(ViewLock.CENTER if key_mods == Qt.ShiftModifier else ViewLock.NONE )
  695.         elif self.op_mode == OpMode.EDIT_PTS:
  696.             self.set_pt_edit_mode(PTEditMode.ADD_DEL if key_mods == Qt.ControlModifier else PTEditMode.MOVE )

  697.     def enterEvent(self, ev):
  698.         super().enterEvent(ev)
  699.         self.mouse_in_widget = True
  700.         self.update_cursor()

  701.     def leaveEvent(self, ev):
  702.         super().leaveEvent(ev)
  703.         self.mouse_in_widget = False
  704.         self.update_cursor()

  705.     def mousePressEvent(self, ev):
  706.         super().mousePressEvent(ev)
  707.         if not self.initialized:
  708.             return

  709.         self.update_mouse_info(QPoint_to_np(ev.pos()))

  710.         btn = ev.button()

  711.         if btn == Qt.LeftButton:
  712.             if self.op_mode == OpMode.NONE:
  713.                 # Clicking in NO OPERATION mode
  714.                 if self.mouse_wire_poly is not None:
  715.                     # Click on wire on any poly -> switch to EDIT_MODE
  716.                     self.set_op_mode(OpMode.EDIT_PTS, op_poly=self.mouse_wire_poly)
  717.                 else:
  718.                     # Click on empty space -> create new poly with one point
  719.                     new_poly = self.ie_polys.add_poly(self.poly_include_type)
  720.                     self.ie_polys.sort()
  721.                     new_poly.add_pt(*self.mouse_img_pt)
  722.                     self.set_op_mode(OpMode.DRAW_PTS, op_poly=new_poly )

  723.             elif self.op_mode == OpMode.DRAW_PTS:
  724.                 # Clicking in DRAW_PTS mode
  725.                 if len(self.op_poly.get_pts()) >= 3 and self.mouse_op_poly_pt_id == 0:
  726.                     # Click on first point -> close poly and switch to edit mode
  727.                     self.set_op_mode(OpMode.EDIT_PTS, op_poly=self.op_poly)
  728.                 else:
  729.                     # Click on empty space -> add point to current poly
  730.                     self.op_poly.add_pt(*self.mouse_img_pt)
  731.                     self.update()

  732.             elif self.op_mode == OpMode.EDIT_PTS:
  733.                 # Clicking in EDIT_PTS mode

  734.                 if self.mouse_op_poly_pt_id is not None:
  735.                     # Click on point of op_poly
  736.                     if self.pt_edit_mode == PTEditMode.ADD_DEL:
  737.                         # in mode 'delete point'
  738.                         self.op_poly.remove_pt(self.mouse_op_poly_pt_id)
  739.                         if self.op_poly.get_pts_count() < 3:
  740.                             # not enough points after delete -> remove poly
  741.                             self.ie_polys.remove_poly (self.op_poly)
  742.                             self.set_op_mode(OpMode.NONE)
  743.                         self.update()

  744.                     elif self.drag_type == DragType.NONE:
  745.                         # otherwise -> start drag
  746.                         self.drag_type = DragType.POLY_PT
  747.                         self.drag_cli_pt     = self.mouse_cli_pt
  748.                         self.drag_poly_pt_id = self.mouse_op_poly_pt_id
  749.                         self.drag_poly_pt    = self.op_poly.get_pts()[ self.drag_poly_pt_id ]
  750.                 elif self.mouse_op_poly_edge_id is not None:
  751.                     # Click on edge of op_poly
  752.                     if self.pt_edit_mode == PTEditMode.ADD_DEL:
  753.                         # in mode 'insert new point'
  754.                         edge_img_pt = self.cli_to_img_pt(self.mouse_op_poly_edge_id_pt)
  755.                         self.op_poly.insert_pt (self.mouse_op_poly_edge_id+1, edge_img_pt)
  756.                         self.update()
  757.                     else:
  758.                         # Otherwise do nothing
  759.                         pass
  760.                 else:
  761.                     # other cases -> unselect poly
  762.                     self.set_op_mode(OpMode.NONE)

  763.         elif btn == Qt.MiddleButton:
  764.             if self.drag_type == DragType.NONE:
  765.                 # Start image drag
  766.                 self.drag_type = DragType.IMAGE_LOOK
  767.                 self.drag_cli_pt = self.mouse_cli_pt
  768.                 self.drag_img_look_pt = self.get_img_look_pt()
  769.                 self.update_cursor()


  770.     def mouseReleaseEvent(self, ev):
  771.         super().mouseReleaseEvent(ev)
  772.         if not self.initialized:
  773.             return

  774.         self.update_mouse_info(QPoint_to_np(ev.pos()))

  775.         btn = ev.button()

  776.         if btn == Qt.LeftButton:
  777.             if self.op_mode == OpMode.EDIT_PTS:
  778.                 if self.drag_type == DragType.POLY_PT:
  779.                     self.drag_type = DragType.NONE
  780.                     self.update()

  781.         elif btn == Qt.MiddleButton:
  782.             if self.drag_type == DragType.IMAGE_LOOK:
  783.                 self.drag_type = DragType.NONE
  784.                 self.update_cursor()
  785.                 self.update()

  786.     def mouseMoveEvent(self, ev):
  787.         super().mouseMoveEvent(ev)
  788.         if not self.initialized:
  789.             return

  790.         prev_mouse_cli_pt = self.mouse_cli_pt
  791.         self.update_mouse_info(QPoint_to_np(ev.pos()))

  792.         if self.view_lock == ViewLock.CENTER:
  793.             if npla.norm(self.mouse_cli_pt - prev_mouse_cli_pt) >= 1:
  794.                 self.img_look_pt = self.mouse_img_pt
  795.                 QCursor.setPos ( self.mapToGlobal( QPoint_from_np(self.img_to_cli_pt(self.img_look_pt)) ))
  796.             self.update()

  797.         if self.drag_type == DragType.IMAGE_LOOK:
  798.             delta_pt = self.cli_to_img_pt(self.mouse_cli_pt) - self.cli_to_img_pt(self.drag_cli_pt)
  799.             self.img_look_pt = self.drag_img_look_pt - delta_pt
  800.             self.update()

  801.         if self.op_mode == OpMode.DRAW_PTS:
  802.             self.update()
  803.         elif self.op_mode == OpMode.EDIT_PTS:
  804.             if self.drag_type == DragType.POLY_PT:
  805.                 delta_pt = self.cli_to_img_pt(self.mouse_cli_pt) - self.cli_to_img_pt(self.drag_cli_pt)
  806.                 self.op_poly.set_point(self.drag_poly_pt_id, self.drag_poly_pt + delta_pt)
  807.                 self.update()

  808.     def wheelEvent(self, ev):
  809.         super().wheelEvent(ev)

  810.         if not self.initialized:
  811.             return

  812.         mods = int(ev.modifiers())
  813.         delta = ev.angleDelta()

  814.         cli_pt = QPoint_to_np(ev.pos())

  815.         if self.drag_type == DragType.NONE:
  816.             sign = np.sign( delta.y() )
  817.             prev_img_pos = self.cli_to_img_pt (cli_pt)
  818.             delta_scale = sign*0.2 + sign * self.get_view_scale() / 10.0
  819.             self.view_scale = np.clip(self.get_view_scale() + delta_scale, 1.0, 20.0)
  820.             new_img_pos = self.cli_to_img_pt (cli_pt)
  821.             if sign > 0:
  822.                 self.img_look_pt = self.get_img_look_pt() + (prev_img_pos-new_img_pos)#*1.5
  823.             else:
  824.                 QCursor.setPos ( self.mapToGlobal(QPoint_from_np(self.img_to_cli_pt(prev_img_pos))) )
  825.             self.update()

  826.     def paintEvent(self, event):
  827.         super().paintEvent(event)
  828.         if not self.initialized:
  829.             return

  830.         qp = self.qp
  831.         qp.begin(self)
  832.         qp.setRenderHint(QPainter.Antialiasing)
  833.         qp.setRenderHint(QPainter.HighQualityAntialiasing)
  834.         qp.setRenderHint(QPainter.SmoothPixmapTransform)

  835.         src_rect = QRect(0, 0, *self.img_size)
  836.         dst_rect = self.img_to_cli_rect( src_rect )

  837.         if self.op_mode == OpMode.VIEW_BAKED:
  838.             qp.drawPixmap(dst_rect, self.img_baked_pixmap, src_rect)
  839.         elif self.op_mode == OpMode.VIEW_XSEG_MASK:
  840.             if self.xseg_mask_pixmap is not None:
  841.                 qp.drawPixmap(dst_rect, self.xseg_mask_pixmap, src_rect)
  842.         else:
  843.             if self.cbar.btn_view_xseg_overlay_mask_act.isChecked() and \
  844.                self.xseg_overlay_mask_pixmap is not None:
  845.                 qp.drawPixmap(dst_rect, self.xseg_overlay_mask_pixmap, src_rect)
  846.             elif self.img_pixmap is not None:
  847.                 qp.drawPixmap(dst_rect, self.img_pixmap, src_rect)

  848.             polys = self.ie_polys.get_polys()
  849.             polys_len = len(polys)

  850.             color_scheme = self.get_current_color_scheme()

  851.             pt_rad = self.canvas_config.pt_radius
  852.             pt_rad_x2 = pt_rad*2

  853.             pt_select_radius = self.canvas_config.pt_select_radius

  854.             op_mode = self.op_mode
  855.             op_poly = self.op_poly

  856.             for i,poly in enumerate(polys):

  857.                 selected_pt_path = QPainterPath()
  858.                 poly_line_path = QPainterPath()
  859.                 pts_line_path = QPainterPath()

  860.                 pt_remove_cli_pt = None
  861.                 poly_pts = poly.get_pts()
  862.                 for pt_id, img_pt in enumerate(poly_pts):
  863.                     cli_pt = self.img_to_cli_pt(img_pt)
  864.                     q_cli_pt = QPoint_from_np(cli_pt)

  865.                     if pt_id == 0:
  866.                         poly_line_path.moveTo(q_cli_pt)
  867.                     else:
  868.                         poly_line_path.lineTo(q_cli_pt)


  869.                     if poly == op_poly:
  870.                         if self.op_mode == OpMode.DRAW_PTS or \
  871.                             (self.op_mode == OpMode.EDIT_PTS and \
  872.                             (self.pt_edit_mode == PTEditMode.MOVE) or \
  873.                             (self.pt_edit_mode == PTEditMode.ADD_DEL and self.mouse_op_poly_pt_id == pt_id) \
  874.                             ):
  875.                             pts_line_path.moveTo( QPoint_from_np(cli_pt + np.float32([0,-pt_rad])) )
  876.                             pts_line_path.lineTo( QPoint_from_np(cli_pt + np.float32([0,pt_rad])) )
  877.                             pts_line_path.moveTo( QPoint_from_np(cli_pt + np.float32([-pt_rad,0])) )
  878.                             pts_line_path.lineTo( QPoint_from_np(cli_pt + np.float32([pt_rad,0])) )

  879.                         if (self.op_mode == OpMode.EDIT_PTS and \
  880.                             self.pt_edit_mode == PTEditMode.ADD_DEL and \
  881.                             self.mouse_op_poly_pt_id == pt_id):
  882.                             pt_remove_cli_pt = cli_pt

  883.                         if self.op_mode == OpMode.DRAW_PTS and \
  884.                             len(op_poly.get_pts()) >= 3 and pt_id == 0 and self.mouse_op_poly_pt_id == pt_id:
  885.                             # Circle around poly point
  886.                             selected_pt_path.addEllipse(q_cli_pt, pt_rad_x2, pt_rad_x2)


  887.                 if poly == op_poly:
  888.                     if op_mode == OpMode.DRAW_PTS:
  889.                         # Line from last point to mouse
  890.                         poly_line_path.lineTo( QPoint_from_np(self.mouse_cli_pt) )

  891.                     if self.mouse_op_poly_pt_id is not None:
  892.                         pass

  893.                     if self.mouse_op_poly_edge_id_pt is not None:
  894.                         if self.pt_edit_mode == PTEditMode.ADD_DEL and self.mouse_op_poly_pt_id is None:
  895.                             # Ready to insert point on edge
  896.                             m_cli_pt = self.mouse_op_poly_edge_id_pt
  897.                             pts_line_path.moveTo( QPoint_from_np(m_cli_pt + np.float32([0,-pt_rad])) )
  898.                             pts_line_path.lineTo( QPoint_from_np(m_cli_pt + np.float32([0,pt_rad])) )
  899.                             pts_line_path.moveTo( QPoint_from_np(m_cli_pt + np.float32([-pt_rad,0])) )
  900.                             pts_line_path.lineTo( QPoint_from_np(m_cli_pt + np.float32([pt_rad,0])) )

  901.                 if len(poly_pts) >= 2:
  902.                     # Closing poly line
  903.                     poly_line_path.lineTo( QPoint_from_np(self.img_to_cli_pt(poly_pts[0])) )

  904.                 # Draw calls
  905.                 qp.setPen(color_scheme.pt_outline_pen)
  906.                 qp.setBrush(QBrush())
  907.                 qp.drawPath(selected_pt_path)

  908.                 qp.setPen(color_scheme.poly_outline_solid_pen)
  909.                 qp.setBrush(QBrush())
  910.                 qp.drawPath(pts_line_path)

  911.                 if poly.get_type() == SegIEPolyType.INCLUDE:
  912.                     qp.setPen(color_scheme.poly_outline_solid_pen)
  913.                 else:
  914.                     qp.setPen(color_scheme.poly_outline_dot_pen)

  915.                 qp.setBrush(color_scheme.poly_unselected_brush)
  916.                 if op_mode == OpMode.NONE:
  917.                     if poly == self.mouse_wire_poly:
  918.                         qp.setBrush(color_scheme.poly_selected_brush)
  919.                 #else:
  920.                 #    if poly == op_poly:
  921.                 #        qp.setBrush(color_scheme.poly_selected_brush)

  922.                 qp.drawPath(poly_line_path)

  923.                 if pt_remove_cli_pt is not None:
  924.                     qp.setPen(color_scheme.poly_outline_solid_pen)
  925.                     qp.setBrush(QBrush())

  926.                     qp.drawLine( *(pt_remove_cli_pt + np.float32([-pt_rad_x2,-pt_rad_x2])), *(pt_remove_cli_pt + np.float32([pt_rad_x2,pt_rad_x2])) )
  927.                     qp.drawLine( *(pt_remove_cli_pt + np.float32([-pt_rad_x2,pt_rad_x2])), *(pt_remove_cli_pt + np.float32([pt_rad_x2,-pt_rad_x2])) )

  928.         qp.end()

  929. class QCanvas(QFrame):
  930.     def __init__(self):
  931.         super().__init__()

  932.         self.canvas_control_left_bar = QCanvasControlsLeftBar()
  933.         self.canvas_control_right_bar = QCanvasControlsRightBar()

  934.         cbar = sn( btn_poly_color_red_act   = self.canvas_control_right_bar.btn_poly_color_red_act,
  935.                    btn_poly_color_green_act = self.canvas_control_right_bar.btn_poly_color_green_act,
  936.                    btn_poly_color_blue_act  = self.canvas_control_right_bar.btn_poly_color_blue_act,
  937.                    btn_view_baked_mask_act  = self.canvas_control_right_bar.btn_view_baked_mask_act,
  938.                    btn_view_xseg_mask_act  = self.canvas_control_right_bar.btn_view_xseg_mask_act,
  939.                    btn_view_xseg_overlay_mask_act  = self.canvas_control_right_bar.btn_view_xseg_overlay_mask_act,
  940.                    btn_poly_color_act_grp = self.canvas_control_right_bar.btn_poly_color_act_grp,
  941.                    btn_view_lock_center_act = self.canvas_control_right_bar.btn_view_lock_center_act,

  942.                    btn_poly_type_include_act = self.canvas_control_left_bar.btn_poly_type_include_act,
  943.                    btn_poly_type_exclude_act = self.canvas_control_left_bar.btn_poly_type_exclude_act,
  944.                    btn_poly_type_act_grp = self.canvas_control_left_bar.btn_poly_type_act_grp,
  945.                    btn_undo_pt_act = self.canvas_control_left_bar.btn_undo_pt_act,
  946.                    btn_redo_pt_act = self.canvas_control_left_bar.btn_redo_pt_act,
  947.                    btn_delete_poly_act = self.canvas_control_left_bar.btn_delete_poly_act,
  948.                    btn_pt_edit_mode_act = self.canvas_control_left_bar.btn_pt_edit_mode_act,
  949.                    btn_auto_detect_act = self.canvas_control_left_bar.btn_auto_detect_act )

  950.         self.op = QCanvasOperator(cbar)
  951.         self.l = QHBoxLayout()
  952.         self.l.setContentsMargins(0,0,0,0)
  953.         self.l.addWidget(self.canvas_control_left_bar)
  954.         self.l.addWidget(self.op)
  955.         self.l.addWidget(self.canvas_control_right_bar)
  956.         self.setLayout(self.l)

  957. class LoaderQSubprocessor(QSubprocessor):
  958.     def __init__(self, image_paths, q_label, q_progressbar, on_finish_func ):

  959.         self.image_paths = image_paths
  960.         self.image_paths_len = len(image_paths)
  961.         self.idxs = [*range(self.image_paths_len)]

  962.         self.filtered_image_paths = self.image_paths.copy()

  963.         self.image_paths_has_ie_polys = { image_path : False for image_path in self.image_paths }

  964.         self.q_label = q_label
  965.         self.q_progressbar = q_progressbar
  966.         self.q_progressbar.setRange(0, self.image_paths_len)
  967.         self.q_progressbar.setValue(0)
  968.         self.q_progressbar.update()
  969.         self.on_finish_func = on_finish_func
  970.         self.done_count = 0
  971.         super().__init__('LoaderQSubprocessor', LoaderQSubprocessor.Cli, 60)

  972.     def get_data(self, host_dict):
  973.         if len (self.idxs) > 0:
  974.             idx = self.idxs.pop(0)
  975.             image_path = self.image_paths[idx]
  976.             self.q_label.setText(f'{QStringDB.loading_tip}... {image_path.name}')

  977.             return idx, image_path

  978.         return None

  979.     def on_clients_finalized(self):
  980.         self.on_finish_func([x for x in self.filtered_image_paths if x is not None], self.image_paths_has_ie_polys)

  981.     def on_data_return (self, host_dict, data):
  982.         self.idxs.insert(0, data[0])

  983.     def on_result (self, host_dict, data, result):
  984.         idx, has_dflimg, has_ie_polys = result

  985.         if not has_dflimg:
  986.             self.filtered_image_paths[idx] = None
  987.         self.image_paths_has_ie_polys[self.image_paths[idx]] = has_ie_polys

  988.         self.done_count += 1
  989.         if self.q_progressbar is not None:
  990.             self.q_progressbar.setValue(self.done_count)

  991.     class Cli(QSubprocessor.Cli):
  992.         def process_data(self, data):
  993.             idx, filename = data
  994.             dflimg = DFLIMG.load(filename)
  995.             if dflimg is not None and dflimg.has_data():
  996.                 ie_polys = dflimg.get_seg_ie_polys()

  997.                 return idx, True, ie_polys.has_polys()
  998.             return idx, False, False

  999. class MainWindow(QXMainWindow):

  1000.     def __init__(self, input_dirpath, cfg_root_path):
  1001.         self.loading_frame = None
  1002.         self.help_frame = None

  1003.         super().__init__()

  1004.         self.input_dirpath = input_dirpath
  1005.         self.trash_dirpath = input_dirpath.parent / (input_dirpath.name + '_trash')
  1006.         self.cfg_root_path = cfg_root_path

  1007.         self.cfg_path = cfg_root_path / 'MainWindow_cfg.dat'
  1008.         self.cfg_dict = pickle.loads(self.cfg_path.read_bytes()) if self.cfg_path.exists() else {}

  1009.         self.cached_images = {}
  1010.         self.cached_has_ie_polys = {}

  1011.         self._occlusion_detector = OcclusionDetector()

  1012.         self.initialize_ui()

  1013.         # Loader
  1014.         self.loading_frame = QFrame(self.main_canvas_frame)
  1015.         self.loading_frame.setAutoFillBackground(True)
  1016.         self.loading_frame.setFrameShape(QFrame.StyledPanel)
  1017.         self.loader_label = QLabel()
  1018.         self.loader_progress_bar = QProgressBar()

  1019.         intro_image = QLabel()
  1020.         intro_image.setPixmap( QPixmap.fromImage(QImageDB.intro) )

  1021.         intro_image_frame_l = QVBoxLayout()
  1022.         intro_image_frame_l.addWidget(intro_image, alignment=Qt.AlignCenter)
  1023.         intro_image_frame = QFrame()
  1024.         intro_image_frame.setSizePolicy (QSizePolicy.Expanding, QSizePolicy.Expanding)
  1025.         intro_image_frame.setLayout(intro_image_frame_l)

  1026.         loading_frame_l = QVBoxLayout()
  1027.         loading_frame_l.addWidget (intro_image_frame)
  1028.         loading_frame_l.addWidget (self.loader_label)
  1029.         loading_frame_l.addWidget (self.loader_progress_bar)
  1030.         self.loading_frame.setLayout(loading_frame_l)

  1031.         self.loader_subprocessor = LoaderQSubprocessor( image_paths=pathex.get_image_paths(input_dirpath, return_Path_class=True),
  1032.                                                         q_label=self.loader_label,
  1033.                                                         q_progressbar=self.loader_progress_bar,
  1034.                                                         on_finish_func=self.on_loader_finish )


  1035.     def on_loader_finish(self, image_paths, image_paths_has_ie_polys):
  1036.         self.image_paths_done = []
  1037.         self.image_paths = image_paths
  1038.         self.image_paths_has_ie_polys = image_paths_has_ie_polys
  1039.         self.set_has_ie_polys_count ( len([ 1 for x in self.image_paths_has_ie_polys if self.image_paths_has_ie_polys[x] == True]) )
  1040.         self.loading_frame.hide()
  1041.         self.loading_frame = None

  1042.         self.process_next_image(first_initialization=True)

  1043.     def closeEvent(self, ev):
  1044.         self._occlusion_detector.shutdown()
  1045.         self.cfg_dict['geometry'] = self.saveGeometry().data()
  1046.         self.cfg_path.write_bytes( pickle.dumps(self.cfg_dict) )


  1047.     def update_cached_images (self, count=5):
  1048.         d = self.cached_images

  1049.         for image_path in self.image_paths_done[:-count]+self.image_paths[count:]:
  1050.             if image_path in d:
  1051.                 del d[image_path]

  1052.         for image_path in self.image_paths[:count]+self.image_paths_done[-count:]:
  1053.             if image_path not in d:
  1054.                 img = cv2_imread(image_path)
  1055.                 if img is not None:
  1056.                     d[image_path] = img

  1057.     def load_image(self, image_path):
  1058.         try:
  1059.             img = self.cached_images.get(image_path, None)
  1060.             if img is None:
  1061.                 img = cv2_imread(image_path)
  1062.                 self.cached_images[image_path] = img
  1063.             if img is None:
  1064.                 io.log_err(f'Unable to load {image_path}')
  1065.         except:
  1066.             img = None

  1067.         return img

  1068.     def update_preview_bar(self):
  1069.         count = self.image_bar.get_preview_images_count()
  1070.         d = self.cached_images
  1071.         prev_imgs = [ d.get(image_path, None) for image_path in self.image_paths_done[-1:-count:-1] ]
  1072.         next_imgs = [ d.get(image_path, None) for image_path in self.image_paths[:count] ]
  1073.         self.image_bar.update_images(prev_imgs, next_imgs)


  1074.     def canvas_initialize(self, image_path, only_has_polys=False):
  1075.         if only_has_polys and not self.image_paths_has_ie_polys[image_path]:
  1076.             return False

  1077.         dflimg = DFLIMG.load(image_path)
  1078.         if not dflimg or not dflimg.has_data():
  1079.             return False

  1080.         ie_polys = dflimg.get_seg_ie_polys()
  1081.         xseg_mask = dflimg.get_xseg_mask()
  1082.         landmarks = dflimg.get_landmarks()
  1083.         img = self.load_image(image_path)
  1084.         if img is None:
  1085.             return False

  1086.         self.canvas.op.initialize ( img,  ie_polys=ie_polys, xseg_mask=xseg_mask, landmarks=landmarks )

  1087.         self.filename_label.setText(f"{image_path.name}")

  1088.         return True

  1089.     def canvas_finalize(self, image_path):
  1090.         self.canvas.op.finalize()

  1091.         if image_path.exists():
  1092.             dflimg = DFLIMG.load(image_path)
  1093.             ie_polys = dflimg.get_seg_ie_polys()
  1094.             new_ie_polys = self.canvas.op.get_ie_polys()

  1095.             if not new_ie_polys.identical(ie_polys):
  1096.                 prev_has_polys = self.image_paths_has_ie_polys[image_path]
  1097.                 self.image_paths_has_ie_polys[image_path] = new_ie_polys.has_polys()
  1098.                 new_has_polys = self.image_paths_has_ie_polys[image_path]

  1099.                 if not prev_has_polys and new_has_polys:
  1100.                     self.set_has_ie_polys_count ( self.get_has_ie_polys_count() +1)
  1101.                 elif prev_has_polys and not new_has_polys:
  1102.                     self.set_has_ie_polys_count ( self.get_has_ie_polys_count() -1)

  1103.                 dflimg.set_seg_ie_polys( new_ie_polys )
  1104.                 dflimg.save()

  1105.         self.filename_label.setText(f"")

  1106.     def process_prev_image(self):
  1107.         key_mods = QApplication.keyboardModifiers()
  1108.         step = 5 if key_mods == Qt.ShiftModifier else 1
  1109.         only_has_polys = key_mods == Qt.ControlModifier

  1110.         if self.canvas.op.is_initialized():
  1111.             self.canvas_finalize(self.image_paths[0])

  1112.         while True:
  1113.             for _ in range(step):
  1114.                 if len(self.image_paths_done) != 0:
  1115.                     self.image_paths.insert (0, self.image_paths_done.pop(-1))
  1116.                 else:
  1117.                     break
  1118.             if len(self.image_paths) == 0:
  1119.                 break

  1120.             ret = self.canvas_initialize(self.image_paths[0], len(self.image_paths_done) != 0 and only_has_polys)

  1121.             if ret or len(self.image_paths_done) == 0:
  1122.                 break

  1123.         self.update_cached_images()
  1124.         self.update_preview_bar()

  1125.     def process_next_image(self, first_initialization=False):
  1126.         key_mods = QApplication.keyboardModifiers()

  1127.         step = 0 if first_initialization else 5 if key_mods == Qt.ShiftModifier else 1
  1128.         only_has_polys = False if first_initialization else key_mods == Qt.ControlModifier

  1129.         if self.canvas.op.is_initialized():
  1130.             self.canvas_finalize(self.image_paths[0])

  1131.         while True:
  1132.             for _ in range(step):
  1133.                 if len(self.image_paths) != 0:
  1134.                     self.image_paths_done.append(self.image_paths.pop(0))
  1135.                 else:
  1136.                     break
  1137.             if len(self.image_paths) == 0:
  1138.                 break
  1139.             if self.canvas_initialize(self.image_paths[0], only_has_polys):
  1140.                 break

  1141.         self.update_cached_images()
  1142.         self.update_preview_bar()
  1143.         
  1144.     def trash_current_image(self):
  1145.         self.process_next_image()
  1146.         
  1147.         img_path = self.image_paths_done.pop(-1)
  1148.         img_path = Path(img_path)
  1149.         self.trash_dirpath.mkdir(parents=True, exist_ok=True)
  1150.         img_path.rename( self.trash_dirpath / img_path.name )
  1151.         
  1152.         self.update_cached_images()
  1153.         self.update_preview_bar()
  1154.         
  1155.     def initialize_ui(self):

  1156.         self.canvas = QCanvas()
  1157.         self.canvas.op._occlusion_detector = self._occlusion_detector

  1158.         image_bar = self.image_bar = ImagePreviewSequenceBar(preview_images_count=9, icon_size=QUIConfig.preview_bar_icon_q_size.width())
  1159.         image_bar.setSizePolicy ( QSizePolicy.Fixed, QSizePolicy.Fixed )


  1160.         btn_prev_image = QXIconButton(QIconDB.left, QStringDB.btn_prev_image_tip, shortcut='A', click_func=self.process_prev_image)
  1161.         btn_prev_image.setIconSize(QUIConfig.preview_bar_icon_q_size)

  1162.         btn_next_image = QXIconButton(QIconDB.right, QStringDB.btn_next_image_tip, shortcut='D', click_func=self.process_next_image)
  1163.         btn_next_image.setIconSize(QUIConfig.preview_bar_icon_q_size)

  1164.         btn_delete_image = QXIconButton(QIconDB.trashcan, QStringDB.btn_delete_image_tip, shortcut='X', click_func=self.trash_current_image)
  1165.         btn_delete_image.setIconSize(QUIConfig.preview_bar_icon_q_size)
  1166.    
  1167.         pad_image = QWidget()
  1168.         pad_image.setFixedSize(QUIConfig.preview_bar_icon_q_size)
  1169.         
  1170.         preview_image_bar_frame_l = QHBoxLayout()
  1171.         preview_image_bar_frame_l.setContentsMargins(0,0,0,0)
  1172.         preview_image_bar_frame_l.addWidget ( pad_image, alignment=Qt.AlignCenter)
  1173.         preview_image_bar_frame_l.addWidget ( btn_prev_image, alignment=Qt.AlignCenter)
  1174.         preview_image_bar_frame_l.addWidget ( image_bar)
  1175.         preview_image_bar_frame_l.addWidget ( btn_next_image, alignment=Qt.AlignCenter)
  1176.         #preview_image_bar_frame_l.addWidget ( btn_delete_image, alignment=Qt.AlignCenter)

  1177.         preview_image_bar_frame = QFrame()
  1178.         preview_image_bar_frame.setSizePolicy ( QSizePolicy.Fixed, QSizePolicy.Fixed )
  1179.         preview_image_bar_frame.setLayout(preview_image_bar_frame_l)

  1180.         preview_image_bar_frame2_l = QHBoxLayout()
  1181.         preview_image_bar_frame2_l.setContentsMargins(0,0,0,0)
  1182.         preview_image_bar_frame2_l.addWidget ( btn_delete_image, alignment=Qt.AlignCenter)

  1183.         preview_image_bar_frame2 = QFrame()
  1184.         preview_image_bar_frame2.setSizePolicy ( QSizePolicy.Fixed, QSizePolicy.Fixed )
  1185.         preview_image_bar_frame2.setLayout(preview_image_bar_frame2_l)
  1186.         
  1187.         preview_image_bar_l = QHBoxLayout()
  1188.         preview_image_bar_l.addWidget (preview_image_bar_frame, alignment=Qt.AlignCenter)
  1189.         preview_image_bar_l.addWidget (preview_image_bar_frame2)
  1190.         
  1191.         preview_image_bar = QFrame()
  1192.         preview_image_bar.setFrameShape(QFrame.StyledPanel)
  1193.         preview_image_bar.setSizePolicy ( QSizePolicy.Expanding, QSizePolicy.Fixed )
  1194.         preview_image_bar.setLayout(preview_image_bar_l)

  1195.         label_font = QFont('Courier New')
  1196.         self.filename_label = QLabel()
  1197.         self.filename_label.setFont(label_font)

  1198.         self.has_ie_polys_count_label = QLabel()

  1199.         status_frame_l = QHBoxLayout()
  1200.         status_frame_l.setContentsMargins(0,0,0,0)
  1201.         status_frame_l.addWidget ( QLabel(), alignment=Qt.AlignCenter)
  1202.         status_frame_l.addWidget (self.filename_label, alignment=Qt.AlignCenter)
  1203.         status_frame_l.addWidget (self.has_ie_polys_count_label, alignment=Qt.AlignCenter)
  1204.         status_frame = QFrame()
  1205.         status_frame.setLayout(status_frame_l)

  1206.         main_canvas_l = QVBoxLayout()
  1207.         main_canvas_l.setContentsMargins(0,0,0,0)
  1208.         main_canvas_l.addWidget (self.canvas)
  1209.         main_canvas_l.addWidget (status_frame)
  1210.         main_canvas_l.addWidget (preview_image_bar)

  1211.         self.main_canvas_frame = QFrame()
  1212.         self.main_canvas_frame.setLayout(main_canvas_l)

  1213.         self.main_l = QHBoxLayout()
  1214.         self.main_l.setContentsMargins(0,0,0,0)
  1215.         self.main_l.addWidget (self.main_canvas_frame)

  1216.         self.setLayout(self.main_l)

  1217.         geometry = self.cfg_dict.get('geometry', None)
  1218.         if geometry is not None:
  1219.             self.restoreGeometry(geometry)
  1220.         else:
  1221.             self.move( QPoint(0,0))

  1222.     def get_has_ie_polys_count(self):
  1223.         return self.has_ie_polys_count

  1224.     def set_has_ie_polys_count(self, c):
  1225.         self.has_ie_polys_count = c
  1226.         self.has_ie_polys_count_label.setText(f"{c} {QStringDB.labeled_tip}")

  1227.     def resizeEvent(self, ev):
  1228.         if self.loading_frame is not None:
  1229.             self.loading_frame.resize( ev.size() )
  1230.         if self.help_frame is not None:
  1231.             self.help_frame.resize( ev.size() )

  1232. def start(input_dirpath):
  1233.     """
  1234.     returns exit_code
  1235.     """
  1236.     io.log_info("Running XSeg editor.")

  1237.     if PackedFaceset.path_contains(input_dirpath):
  1238.         io.log_info (f'\n{input_dirpath} contains packed faceset! Unpack it first.\n')
  1239.         return 1

  1240.     root_path = Path(__file__).parent
  1241.     cfg_root_path = Path(tempfile.gettempdir())

  1242.     QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
  1243.     QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)

  1244.     app = QApplication([])
  1245.     app.setApplicationName("XSegEditor")
  1246.     app.setStyle('Fusion')

  1247.     QFontDatabase.addApplicationFont( str(root_path / 'gfx' / 'fonts' / 'NotoSans-Medium.ttf') )

  1248.     app.setFont( QFont('NotoSans'))

  1249.     QUIConfig.initialize()
  1250.     QStringDB.initialize()

  1251.     QIconDB.initialize( root_path / 'gfx' / 'icons' )
  1252.     QCursorDB.initialize( root_path / 'gfx' / 'cursors' )
  1253.     QImageDB.initialize( root_path / 'gfx' / 'images' )

  1254.     app.setWindowIcon(QIconDB.app_icon)
  1255.     app.setPalette( QDarkPalette() )

  1256.     win = MainWindow( input_dirpath=input_dirpath, cfg_root_path=cfg_root_path)

  1257.     win.show()
  1258.     win.raise_()

  1259.     app.exec_()
  1260.     return 0
复制代码


XSegEditor/QIconDB.py

  1. from PyQt5.QtCore import *
  2. from PyQt5.QtGui import *
  3. from PyQt5.QtWidgets import *


  4. class QIconDB():
  5.     @staticmethod
  6.     def initialize(icon_path):
  7.         QIconDB.app_icon         = QIcon ( str(icon_path / 'app_icon.png') )
  8.         QIconDB.delete_poly      = QIcon ( str(icon_path / 'delete_poly.png') )
  9.         QIconDB.undo_pt          = QIcon ( str(icon_path / 'undo_pt.png') )
  10.         QIconDB.redo_pt          = QIcon ( str(icon_path / 'redo_pt.png') )
  11.         QIconDB.poly_color_red   = QIcon ( str(icon_path / 'poly_color_red.png') )
  12.         QIconDB.poly_color_green = QIcon ( str(icon_path / 'poly_color_green.png') )
  13.         QIconDB.poly_color_blue  = QIcon ( str(icon_path / 'poly_color_blue.png') )
  14.         QIconDB.poly_type_include = QIcon ( str(icon_path / 'poly_type_include.png') )
  15.         QIconDB.poly_type_exclude = QIcon ( str(icon_path / 'poly_type_exclude.png') )
  16.         QIconDB.left  = QIcon ( str(icon_path / 'left.png') )
  17.         QIconDB.right = QIcon ( str(icon_path / 'right.png') )
  18.         QIconDB.trashcan = QIcon ( str(icon_path / 'trashcan.png') )
  19.         QIconDB.pt_edit_mode = QIcon ( str(icon_path / 'pt_edit_mode.png') )
  20.         QIconDB.view_lock_center = QIcon ( str(icon_path / 'view_lock_center.png') )
  21.         QIconDB.view_baked = QIcon ( str(icon_path / 'view_baked.png') )
  22.         QIconDB.view_xseg = QIcon ( str(icon_path / 'view_xseg.png') )
  23.         QIconDB.view_xseg_overlay = QIcon ( str(icon_path / 'view_xseg_overlay.png') )
  24.         QIconDB.auto_detect = QIcon ( str(icon_path / 'auto_detect.png') )
  25.         
复制代码
XSegEditor/QStringDB.py
  1. from localization import system_language


  2. class QStringDB():
  3.    
  4.     @staticmethod
  5.     def initialize():
  6.         lang = system_language
  7.         
  8.         if lang not in ['en','ru','zh']:
  9.             lang = 'en'
  10.         
  11.         QStringDB.btn_poly_color_red_tip = {    'en' : 'Poly color scheme red',
  12.                                                 'ru' : 'Красная цветовая схема полигонов',
  13.                                                 'zh' : '选区配色方案红色',
  14.                                            }[lang]
  15.                                           
  16.         QStringDB.btn_poly_color_green_tip = {  'en' : 'Poly color scheme green',
  17.                                                 'ru' : 'Зелёная цветовая схема полигонов',
  18.                                                 'zh' : '选区配色方案绿色',
  19.                                            }[lang]
  20.                                           
  21.         QStringDB.btn_poly_color_blue_tip = {   'en' : 'Poly color scheme blue',
  22.                                                 'ru' : 'Синяя цветовая схема полигонов',
  23.                                                 'zh' : '选区配色方案蓝色',
  24.                                            }[lang]
  25.                                           
  26.         QStringDB.btn_view_baked_mask_tip = {   'en' : 'View baked mask',
  27.                                                 'ru' : 'Посмотреть запечёную маску',
  28.                                                 'zh' : '查看遮罩通道',
  29.                                            }[lang]
  30.                                           
  31.         QStringDB.btn_view_xseg_mask_tip =  {   'en' : 'View trained XSeg mask',
  32.                                                 'ru' : 'Посмотреть тренированную XSeg маску',
  33.                                                 'zh' : '查看导入后的XSeg遮罩',
  34.                                             }[lang]
  35.                                             
  36.         QStringDB.btn_view_xseg_overlay_mask_tip =  {   'en' : 'View trained XSeg mask overlay face',
  37.                                                         'ru' : 'Посмотреть тренированную XSeg маску поверх лица',
  38.                                                         'zh' : '查看导入后的XSeg遮罩于脸上方',
  39.                                                     }[lang]
  40.                                           
  41.         QStringDB.btn_poly_type_include_tip = { 'en' : 'Poly include mode',
  42.                                                 'ru' : 'Режим полигонов - включение',
  43.                                                 'zh' : '包含选区模式',
  44.                                            }[lang]
  45.                                           
  46.         QStringDB.btn_poly_type_exclude_tip = { 'en' : 'Poly exclude mode',
  47.                                                 'ru' : 'Режим полигонов - исключение',
  48.                                                 'zh' : '排除选区模式',
  49.                                            }[lang]        
  50.                                           
  51.         QStringDB.btn_undo_pt_tip = {   'en' : 'Undo point',
  52.                                         'ru' : 'Отменить точку',
  53.                                         'zh' : '撤消点',
  54.                                     }[lang]      
  55.                                     
  56.         QStringDB.btn_redo_pt_tip = {   'en' : 'Redo point',
  57.                                         'ru' : 'Повторить точку',
  58.                                         'zh' : '重做点',
  59.                                      }[lang]      
  60.                                       
  61.         QStringDB.btn_delete_poly_tip = {   'en' : 'Delete poly',
  62.                                             'ru' : 'Удалить полигон',
  63.                                             'zh' : '删除选区',
  64.                                            }[lang]     
  65.                                              
  66.         QStringDB.btn_pt_edit_mode_tip = {  'en' : 'Add/delete point mode ( HOLD CTRL )',
  67.                                             'ru' : 'Режим добавления/удаления точек ( удерживайте CTRL )',
  68.                                             'zh' : '点加/删除模式 ( 按住CTRL )',
  69.                                            }[lang]   
  70.                                           
  71.         QStringDB.btn_view_lock_center_tip = { 'en' : 'Lock cursor at the center ( HOLD SHIFT )',
  72.                                                'ru' : 'Заблокировать курсор в центре ( удерживайте SHIFT )',
  73.                                                'zh' : '将光标锁定在中心 ( 按住SHIFT )',
  74.                                              }[lang]                    
  75.                                        
  76.                                           
  77.         QStringDB.btn_prev_image_tip = {    'en' : 'Save and Prev image\nHold SHIFT : accelerate\nHold CTRL : skip non masked\n',
  78.                                             'ru' : 'Сохранить и предыдущее изображение\nУдерживать SHIFT : ускорить\nУдерживать CTRL : пропустить неразмеченные\n',
  79.                                             'zh' : '保存并转到上一张图片\n按住SHIFT : 加快\n按住CTRL : 跳过未标记的\n',
  80.                                            }[lang]   
  81.         QStringDB.btn_next_image_tip = {    'en' : 'Save and Next image\nHold SHIFT : accelerate\nHold CTRL : skip non masked\n',
  82.                                             'ru' : 'Сохранить и следующее изображение\nУдерживать SHIFT : ускорить\nУдерживать CTRL : пропустить неразмеченные\n',
  83.                                             'zh' : '保存并转到下一张图片\n按住SHIFT : 加快\n按住CTRL : 跳过未标记的\n',
  84.                                            }[lang]  
  85.         
  86.         QStringDB.btn_delete_image_tip = {  'en' : 'Move to _trash and Next image\n',
  87.                                             'ru' : 'Переместить в _trash и следующее изображение\n',
  88.                                             'zh' : '移至_trash,转到下一张图片 ',
  89.                                            }[lang]  
  90.                                           
  91.         QStringDB.loading_tip = {'en' : 'Loading',
  92.                                  'ru' : 'Загрузка',
  93.                                  'zh' : '正在载入',
  94.                                 }[lang]   
  95.                                 
  96.         QStringDB.labeled_tip = {'en' : 'labeled',
  97.                                  'ru' : 'размечено',
  98.                                  'zh' : '标记的',
  99.                                 }[lang]

  100.         QStringDB.btn_auto_detect_tip = {'en' : 'Auto detect occlusion',
  101.                                          'ru' : 'Автообнаружение окклюзии',
  102.                                          'zh' : '自动检测遮挡物',
  103.                                         }[lang]   
  104.                                           
复制代码

如果不会修改就用我网盘里面存的文件覆盖你原始的文件就行了,

通过网盘分享的文件:
链接: https://pan.baidu.com/s/12lH1u11mEnDDYnV75_ZK5g?pwd=t8i8 提取码: t8i8 复制这段内容后打开百度网盘手机App,操作更方便哦

你看看你的python里面有没有这个onnxruntime库,正确的安装方法是打开cmd ,然后用python安装,比方说我的python路径是D:\AI\DeepFaceLab\_internal\python\,所以在cmd中运行“ D:/AI/DeepFaceLab/_internal/python/python.exe -m pip install onnxruntime -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com”,记住了是你dfl的python环境,不是电脑的python环境,不知道 的看看你setenv.bat里面的python目录


在这里我说明一点:假如要实现更高效的更高级的,更准确的遮罩,就必须用pytorch了,因为现有的所有的高级人脸解析都是建立在pytorch基础上的,这也是为什么很多人希望把pytorch替换dfl的tf框架。


评分

参与人数 1贡献 +1 收起 理由
WaveBedo + 1 感谢楼主分享

查看全部评分

回复

使用道具 举报

44

主题

1063

帖子

6682

积分

高级丹圣

Rank: 13Rank: 13Rank: 13Rank: 13

积分
6682

万事如意节日勋章开心娱乐节日勋章

发表于 昨天 22:58 | 显示全部楼层
本帖最后由 pasanonic 于 2026-7-15 23:01 编辑
day270010678 发表于 2026-7-15 22:56
是按照到你dfl的python环境里面,setenv.bat里面的python环境。不是你自己电脑的python ...



大佬牛B  好了,自动画了  就是线条不太圆润,头部意外的有些多余,不过我要求不高,没那么追求精细
QQ20260715-225658.png


回复 支持 0 反对 1

使用道具 举报

44

主题

1063

帖子

6682

积分

高级丹圣

Rank: 13Rank: 13Rank: 13Rank: 13

积分
6682

万事如意节日勋章开心娱乐节日勋章

发表于 昨天 22:36 | 显示全部楼层
day270010678 发表于 2026-7-15 22:35
你装的是那个版本?是gpu版本,还是就是我上面说的?尽量不要用gpu版本,我当时就是怕出现问题,所以没有 ...

pip install onnxruntime
回复 支持 1 反对 0

使用道具 举报

44

主题

1063

帖子

6682

积分

高级丹圣

Rank: 13Rank: 13Rank: 13Rank: 13

积分
6682

万事如意节日勋章开心娱乐节日勋章

发表于 昨天 22:33 | 显示全部楼层
本帖最后由 pasanonic 于 2026-7-15 22:35 编辑
day270010678 发表于 2026-7-15 22:27
你安装onnxruntime库了吗?

安装了  本来就有 后来还升级了 1.26  不过我是amd卡, 装的cpu推理  
回复 支持 1 反对 0

使用道具 举报

91

主题

1686

帖子

8755

积分

高级丹圣

Rank: 13Rank: 13Rank: 13Rank: 13

积分
8755

万事如意节日勋章开心娱乐节日勋章

发表于 昨天 22:21 | 显示全部楼层
day270010678 发表于 2026-7-15 21:09
我把嘴唇区域排除用虚线标出来了,主要是为了防止嘴巴含东西,你们不需要的话,直接删除就行了,还有一个小 ...

图3图4有部分头发没圈出去,影响会很大,所以一般遇到这样的我都是手动画,直接从眉毛上打横线不走头发轮廓,楼主可以改成这样的吗?如果能改就好了
图12无遮挡部分那种其实万能遮罩应用即可
回复 支持 1 反对 0

使用道具 举报

44

主题

1063

帖子

6682

积分

高级丹圣

Rank: 13Rank: 13Rank: 13Rank: 13

积分
6682

万事如意节日勋章开心娱乐节日勋章

发表于 昨天 22:17 | 显示全部楼层
本帖最后由 pasanonic 于 2026-7-15 22:24 编辑

所有步骤都做了 按了左下角图标没反应

QQ20260715-221635.png


QQ20260715-221832.png QQ20260715-221843.png
回复 支持 1 反对 0

使用道具 举报

33

主题

223

帖子

1694

积分

初级丹圣

Rank: 8Rank: 8

积分
1694
 楼主| 发表于 昨天 21:09 | 显示全部楼层
我把嘴唇区域排除用虚线标出来了,主要是为了防止嘴巴含东西,你们不需要的话,直接删除就行了,还有一个小图标,就是这个放到XSegEditor/gfx/icons/auto_detect.png目录里面就行了
auto_detect.png
回复 支持 反对

使用道具 举报

33

主题

223

帖子

1694

积分

初级丹圣

Rank: 8Rank: 8

积分
1694
 楼主| 发表于 昨天 22:27 | 显示全部楼层
pasanonic 发表于 2026-7-15 22:17
所有步骤都做了 按了左下角图标没反应

你安装onnxruntime库了吗?
回复 支持 反对

使用道具 举报

33

主题

223

帖子

1694

积分

初级丹圣

Rank: 8Rank: 8

积分
1694
 楼主| 发表于 昨天 22:30 | 显示全部楼层
mzh920 发表于 2026-7-15 22:21
图3图4有部分头发没圈出去,影响会很大,所以一般遇到这样的我都是手动画,直接从眉毛上打横线不走头发轮 ...

有些东西需要精修的,你说的那些手动修改下就行了,理论上是像素级别的,所以只需要手动修改部分就行了。总比手一个个画要省事的多。等我空了最多我把嘴巴模型升级下。其他的不升级了,就靠官方的模型就行了
回复 支持 反对

使用道具 举报

33

主题

223

帖子

1694

积分

初级丹圣

Rank: 8Rank: 8

积分
1694
 楼主| 发表于 昨天 22:35 | 显示全部楼层
pasanonic 发表于 2026-7-15 22:33
安装了  本来就有 后来还升级了

你装的是那个版本?是gpu版本,还是就是我上面说的?尽量不要用gpu版本,我当时就是怕出现问题,所以没有用gpu版本。
回复 支持 反对

使用道具 举报

33

主题

223

帖子

1694

积分

初级丹圣

Rank: 8Rank: 8

积分
1694
 楼主| 发表于 昨天 22:42 | 显示全部楼层
pasanonic 发表于 2026-7-15 22:36
pip install onnxruntime

通过网盘分享的文件:
链接: https://pan.baidu.com/s/12lH1u11mEnDDYnV75_ZK5g?pwd=t8i8 提取码: t8i8 复制这段内容后打开百度网盘手机App,操作更方便哦

你下载我修改的测试下,我觉得你是按照库导致的问题, 你看看你的python里面有没有这个onnxruntime库,正确的安装方法是打开cmd ,然后用python安装,比方说我的python路径是D:\AI\DeepFaceLab\_internal\python\,所以在cmd中运行“ D:/AI/DeepFaceLab/_internal/python/python.exe -m pip install onnxruntime -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com”
回复 支持 反对

使用道具 举报

QQ|Archiver|手机版|deepfacelab中文网 |网站地图

GMT+8, 2026-7-16 04:50 , Processed in 0.135453 second(s), 37 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表