初始化换发型项目:3个微服务代码 + 部署脚本

包含:
- hair_service_sd: 换发型/换发色算法服务 (端口 8801)
- photo_service: LoRA 训练调度服务 (端口 32678)
- stable-diffusion-webui: SD WebUI 推理服务 (端口 57860)
- kohya_ss_home: 训练环境代码
- meidaojia: 监控测试脚本
- setup.sh: 一键部署脚本 (conda环境恢复 + 配置生成 + 完整性检查)
- start_all_services.sh: 启动3个服务
- configure.ini.template: 路径模板化 (BASE_DIR自动推导)
- conda_envs/py310.yml: py310 环境定义

大文件 (weights/, models/, data/, conda_envs/*.tar.gz 等) 通过 .gitignore 排除,
由网盘单独上传。
This commit is contained in:
colomi
2026-07-11 18:11:49 +08:00
commit 0eb61f3e60
628 changed files with 120882 additions and 0 deletions
+250
View File
@@ -0,0 +1,250 @@
import torch
import torch.onnx
import torch.nn as nn
import sys
import os
from momocv.resnet import resnet18
import re
import numpy as np
import cv2
from sklearn import linear_model
from tqdm import tqdm
from momocv.LeftEye import get_left_eye_symbol
from utils import landmark_processor
from mtcnn.detector import MTCNNFaceDetector
import math
class ResNet18(nn.Module):
def __init__(self, out_channels):
super(ResNet18, self).__init__()
self.op_name = 'ResNet18'
self.res18 = resnet18(pretrained=False)
self.res18.fc = nn.Linear(512, out_channels)
def forward(self, x):
ret = self.res18(x)
return ret
def get_full_face_symbol(output_nc=137 * 2):
return ResNet18(output_nc)
class Model137(nn.Module):
def __init__(self, gpu_id=None):
super(Model137, self).__init__()
self.device = torch.device('cuda:{}'.format(gpu_id) if gpu_id is not None else 'cpu')
self.face_alignment_net = ResNet18(137 * 2)
self.model_path, _ = os.path.split(os.path.realpath(__file__))
weights = torch.load(os.path.join(self.model_path, 'FullFace.pth'),
map_location=lambda storage, loc: storage)
self.face_alignment_net.load_state_dict(weights)
self.to(self.device)
self.eval()
def forward(self, imgs):
pred_key_pts = self.face_alignment_net(imgs)
return pred_key_pts
class MomocvFaceAlignmentFinalV1(object):
def __init__(self, predict_eye=False, gpu_id=None):
self.gpu_id = gpu_id
self.device = torch.device('cuda:{}'.format(gpu_id) if gpu_id is not None else 'cpu')
self.predict_eye = predict_eye
self.face_alignment_net = get_full_face_symbol()
self.model_path, _ = os.path.split(os.path.realpath(__file__))
weights = torch.load(os.path.join(self.model_path, 'FullFace.pth'),
map_location=lambda storage, loc: storage)
self.face_alignment_net.load_state_dict(weights)
self.face_alignment_net.to(self.device)
self.face_alignment_net.eval()
if predict_eye:
self.eye_alignment_net = get_left_eye_symbol()
self.model_path, _ = os.path.split(os.path.realpath(__file__))
weights = torch.load(os.path.join(self.model_path, 'LeftEye.pth'),
map_location=lambda storage, loc: storage)
self.eye_alignment_net.load_state_dict(weights)
self.eye_alignment_net.to(self.device)
self.eye_alignment_net.eval()
self.trackingFaceRects = []
print('conansherry MomocvFaceAlignmentFinalV1')
def forward(self, img_tensor):
fullyconnected1 = self.face_alignment_net(img_tensor).detach().cpu().numpy()
return fullyconnected1
def detect(self, img, landmarks):
dst_size = 256
landmarks_res = []
with torch.no_grad():
input_numpy = np.zeros((len(landmarks), 3, dst_size, dst_size), dtype=np.float32)
all_mat = []
for ix, landmark in enumerate(landmarks):
M = landmark_processor.get_transform_mat_mmcv_bigger(landmark, dst_size)
all_mat.append(M)
tmp = cv2.warpAffine(img, M, (dst_size, dst_size))
input_numpy[ix, :, :, :] = tmp.transpose((2, 0, 1)).astype(np.float32) / 255
in_tensor = torch.from_numpy(input_numpy)
in_tensor = in_tensor.to(self.device)
fullyconnected1 = self.face_alignment_net(in_tensor).detach().cpu().numpy()
for ix, pts in enumerate(fullyconnected1):
orig_pts = (np.reshape(pts, (2, 137)).transpose((1, 0)) * dst_size)
orig_pts = landmark_processor.transform_points(orig_pts, all_mat[ix], invert=True)
if self.predict_eye:
eye_landmark = self.detect_eye(img, orig_pts)
orig_pts[87:104] = eye_landmark[0]
orig_pts[104:121] = eye_landmark[1]
landmarks_res.append(orig_pts)
return landmarks_res
def detect_eye(self, img, landmarks):
dst_size = 96
src_len = cv2.norm(landmarks[96] - landmarks[88])
dst_len = 96 * 0.7
degree = math.atan2(landmarks[88, 1] - landmarks[96, 1], landmarks[88, 0] - landmarks[96, 0])
src_center = (landmarks[88] + landmarks[96]) / 2
offset = np.array([0.5, 0.5]) * 96 - src_center
left_M = cv2.getRotationMatrix2D((src_center[0], src_center[1]), math.degrees(degree), dst_len / src_len)
left_M[:, 2] += offset
left_eye_img = cv2.warpAffine(img, left_M, (dst_size, dst_size))
dst_size = 96
src_len = cv2.norm(landmarks[105] - landmarks[113])
dst_len = 96 * 0.7
degree = math.atan2(landmarks[113, 1] - landmarks[105, 1], landmarks[113, 0] - landmarks[105, 0])
src_center = (landmarks[105] + landmarks[113]) / 2
offset = np.array([0.5, 0.5]) * 96 - src_center
right_M = cv2.getRotationMatrix2D((src_center[0], src_center[1]), math.degrees(degree), dst_len / src_len)
right_M[:, 2] += offset
right_eye_img = cv2.warpAffine(img, right_M, (dst_size, dst_size))
right_eye_img = cv2.flip(right_eye_img, 1)
# cv2.imshow('left_eye_img', left_eye_img)
# cv2.imshow('right_eye_img', right_eye_img)
with torch.no_grad():
input_numpy = np.zeros((2, 3, dst_size, dst_size), dtype=np.float32)
input_numpy[0, :, :, :] = left_eye_img.transpose((2, 0, 1)).astype(np.float32) / 255
input_numpy[1, :, :, :] = right_eye_img.transpose((2, 0, 1)).astype(np.float32) / 255
in_tensor = torch.from_numpy(input_numpy)
in_tensor = in_tensor.to(self.device)
fullyconnected1 = self.eye_alignment_net(in_tensor).detach().cpu().numpy()
landmarks_res = []
all_mat = [left_M, right_M]
for ix, pts in enumerate(fullyconnected1):
orig_pts = (np.reshape(pts, (2, 17)).transpose((1, 0)) * dst_size)
if ix == 1:
orig_pts[:, 0] = dst_size - orig_pts[:, 0]
orig_pts = landmark_processor.transform_points(orig_pts, all_mat[ix], invert=True)
landmarks_res.append(orig_pts)
return landmarks_res
# cv2.waitKey()
def stable_forward(self, image, detected_faces, reset=False):
if reset is True:
self.trackingFaceRects = []
if len(self.trackingFaceRects) == 0:
for face_rect in detected_faces:
new_tracking_rect = [face_rect, True, [0, 0], 0, None]
self.trackingFaceRects.append(new_tracking_rect)
with torch.no_grad():
landmarks = []
eye_landmarks = []
for tracking_face_rect in self.trackingFaceRects:
if tracking_face_rect[1] == True:
d = tracking_face_rect[0]
src_center = np.array([d[2] - (d[2] - d[0]) / 2.0, d[3] - (d[3] - d[1]) / 2.0])
rotate_degree = tracking_face_rect[3]
scale = 256 * 0.6 / min(d[2] - d[0], d[3] - d[1])
dst_center = np.array([0.5, 0.5]) * 256
offset = dst_center - src_center
M = cv2.getRotationMatrix2D((src_center[0], src_center[1]), rotate_degree, scale)
M[:, 2] += offset
else:
rotate_degree = 0
M = landmark_processor.get_transform_mat_mmcv_bigger(tracking_face_rect[4], 256)
inp = cv2.warpAffine(image, M, (256, 256))
cv2.imshow('inp', inp)
orig_inp = inp
inp = inp.transpose((2, 0, 1)).astype(np.float32)
inp = inp[np.newaxis, :, :, :] / 255
t0 = cv2.getTickCount()
in_tensor = torch.from_numpy(inp)
in_tensor = in_tensor.to(self.device)
fullyconnected1 = self.face_alignment_net(in_tensor).detach().cpu().numpy()
fullyconnected1 = fullyconnected1[0]
orig_pts = (np.reshape(fullyconnected1, (2, 137)).transpose((1, 0)) * 256)
t2 = cv2.getTickCount()
orig_pts = landmark_processor.transform_points(orig_pts, M, invert=True)
if self.predict_eye:
eye_landmark = self.detect_eye(image, orig_pts)
orig_pts[87:104] = eye_landmark[0]
orig_pts[104:121] = eye_landmark[1]
eye_landmarks.append(eye_landmark)
# orig_pts = orig_pts.transpose((1, 0))
fullyconnected1 = orig_pts
# update tracking infos
tracking_face_rect[1] = False
tracking_face_rect[2] = [fullyconnected1[68], fullyconnected1[74], fullyconnected1[96], fullyconnected1[113]]
tracking_face_rect[3] = rotate_degree
tracking_face_rect[4] = fullyconnected1
landmarks.append(fullyconnected1)
return landmarks, eye_landmarks
if __name__=='__main__':
gpu_id = 0
net = MomocvFaceAlignmentFinalV1(predict_eye=True, gpu_id=gpu_id)
video_name = r'G:\all_online_videos\2019_04_09_21_57_25_10342e28-ed60-4568-8790-5a4431384031_Trim.mp4'
cap = cv2.VideoCapture(video_name)
face_detector = MTCNNFaceDetector(gpu_id=gpu_id)
reset = False
bounding_boxes = []
while True:
_, in_frame = cap.read()
if in_frame is None:
cap = cv2.VideoCapture(video_name)
_, in_frame = cap.read()
if len(bounding_boxes) == 0 or reset:
bounding_boxes, landmarks = face_detector.forward(in_frame, min_face_size=100, thresholds=[0.8, 0.9, 0.95])
# for box_score in bounding_boxes:
# cv2.rectangle(in_frame, (int(box_score[0]), int(box_score[1])),
# (int(box_score[2]), int(box_score[3])),
# (0, 255, 0),
# 2)
#
# for pt in landmarks:
# for i in range(5):
# cv2.circle(in_frame, (int(pt[i]), int(pt[i + 5])), 1, (255, 0, 0), 2)
for _ in range(3):
landmark137, eye_landmark = net.stable_forward(in_frame, bounding_boxes, reset)
reset = False
for pts in landmark137:
for ix, pt in enumerate(pts):
# cv2.putText(in_frame, str(ix), (int(pt[0]), int(pt[1])), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (255, 0, 0))
cv2.circle(in_frame, (pt[0], pt[1]), 1, (0, 255, 0), 1)
# for pts in eye_landmark:
# for eye in pts:
# for pt in eye:
# cv2.circle(in_frame, (pt[0], pt[1]), 1, (0, 0, 255), 1)
cv2.imshow('in_frame', in_frame)
key = cv2.waitKey(10)
if key == ord('r'):
reset = True
bounding_boxes = []