浏览代码

feat: add fury. run successfully

Shellmiao 2 年之前
父节点
当前提交
f0a19a538b
共有 10 个文件被更改,包括 141 次插入44 次删除
  1. 1 1
      .env
  2. 4 0
      animal_options.json
  3. 56 0
      api/img2img.py
  4. 55 2
      handler/card_handler.py
  5. 3 10
      template/choose_card.json
  6. 2 9
      template/no_img_card.json
  7. 6 9
      template/start_card.json
  8. 4 10
      template/upload_card.json
  9. 8 1
      utils/config_io.py
  10. 2 2
      utils/template_io.py

+ 1 - 1
.env

@@ -2,4 +2,4 @@ BOT_AES_KEY=ti9qtV3qofRTYjIrN0clnhHcoQvpHXIP
 app_id=cli_a4352ec26db89013
 app_secret=EBzvyQmEx6WlqS8FkUimUhpNbFyhnwlF
 segment_url=https://u166586-afba-837b5bd8.westa.seetacloud.com:8443
-img_url=https://u166586-a3a7-76a835bb.westb.seetacloud.com:8443
+img_url=https://u166586-b297-3612b115.westb.seetacloud.com:8443

+ 4 - 0
animal_options.json

@@ -0,0 +1,4 @@
+{
+    "老虎": "tiger",
+    "狮子": "lion"
+}

+ 56 - 0
api/img2img.py

@@ -71,6 +71,62 @@ def img_2_img(prompt, mask_img, img_key, img_path, output_directory):
     }
     response = requests.post(url=f'{img_url}/sdapi/v1/img2img', json=payload)
 
+    r = response.json()
+    output_path = output_directory + f"/{img_key}-output.jpg"
+    try:
+        result = r['images'][0]
+        image = Image.open(io.BytesIO(base64.b64decode(result.split(",", 1)[0])))
+        image.save(output_path)
+        return output_path
+    except:
+        logger.error('img2img error' + str(r))
+        return None
+    
+def img_2_furry(prompt, img_key, img_path, output_directory):
+    with open(img_path, 'rb') as f:
+        image_data = f.read()
+    input_image_pil = Image.open(io.BytesIO(image_data))
+
+    input_image = base64.b64encode(image_data).decode('utf-8')
+    payload = {
+        "init_images": [
+            input_image
+        ],
+        "denoising_strength": 0.76,
+        "resize_mode": 1,
+        "width": 512,
+        "height": 512,
+        "prompt": prompt,
+        "negative_prompt": "(worst quality, low quality:2),NSFW,monochrome,zombie,overexposure,watermark,text,bad anatomy,bad hand,extra hands,extra fingers,too many fingers,fused fingers,bad arm,distorted arm,extra arms,fused arms,extra legs,missing leg,disembodied leg,extra nipples,detached arm,liquid hand,inverted hand,disembodied limb,small breasts,loli,oversized head,extra body,completely nude,extra navel,EasyNegative,(hair between eyes),sketch,duplicate,ugly,huge eyes,text,logo,worst face,(bad and mutated hands:1.3),(blurry:2),horror,geometry,bad_prompt,(bad hands),(missing fingers),multiple limbs,bad anatomy,(interlocked fingers:1.2),Ugly Fingers,(extra digit and hands and fingers and legs and arms:1.4),((2girl)),(deformed fingers:1.2),(long fingers:1.2),(bad-artist-anime),bad-artist,bad hand,extra legs",
+        "batch_size": 1,
+        "steps": 20,
+        "cfg_scale": 7,
+        "sampler_index": "Euler a",
+        "alwayson_scripts": {
+            "controlnet": {
+                "args": [
+                    {
+                        "input_image": input_image,
+                        "module": "openpose_full",
+                        "model": "control_v11p_sd15_openpose [cab727d4]",
+                        "weight": 0.25,
+                        "starting_control_step": 0,
+                        "ending_control_step": 0.36,
+                    },
+                    {
+                        "input_image": input_image,
+                        "module": "canny",
+                        "model": "control_v11p_sd15_canny [d14c016b]",
+                        "weight": 0.25,
+                        "starting_control_step": 0,
+                        "ending_control_step": 0.36,
+                    }
+                ]
+            }
+        }
+    }
+    response = requests.post(url=f'{img_url}/sdapi/v1/img2img', json=payload)
+
     r = response.json()
     output_path = output_directory + f"/{img_key}-output.jpg"
     try:

+ 55 - 2
handler/card_handler.py

@@ -1,7 +1,7 @@
 import json
 import threading
 from concurrent.futures import ThreadPoolExecutor
-from api.img2img import img_2_img
+from api.img2img import img_2_img, img_2_furry
 from api.segment import get_segment_mask
 from utils.config_io import load_config_from_json, save_config_to_json
 from utils.image_io import get_img_file, get_img_key_from_input_images
@@ -16,6 +16,7 @@ ACTION_TO_TITLE = {
 }
 
 clothes_lock = threading.Lock()
+furry_lock = threading.Lock()
 
 def handle_card(card_data):
     reply_message_id = card_data['open_message_id']
@@ -33,11 +34,17 @@ def handle_card(card_data):
         # TODO: 保存任务状态实现断点继续
         if action['value']['key'] == 'start':
             config_data = load_config_from_json(reply_message_id)
+            logger.info('用户点击了开始按钮')
             if config_data['action'] != "" and config_data['lora'] != "":
                 response_data = waiting_card(reply_message_id, config_data)
                 executor = ThreadPoolExecutor()
                 executor.submit(start_sd_process, reply_message_id, config_data)
                 return response_data
+            if config_data['action'] != "" and config_data['animal'] != "":
+                response_data = waiting_card(reply_message_id, config_data)
+                executor = ThreadPoolExecutor()
+                executor.submit(start_sd_process, reply_message_id, config_data)
+                return response_data
         else:
             logger.info('用户点击了上传中按钮')
 
@@ -47,8 +54,29 @@ def start_sd_process(reply_message_id, config_data):
         # 获取segment mask
         start_clothes_img_theards(reply_message_id)
     else:
+        start_furry_img_theards(reply_message_id)
         pass
 
+# 开始furry的每个图片的线程
+def start_furry_img_theards(reply_message_id):
+    img_keys = get_img_key_from_input_images(reply_message_id)
+
+    config_data = load_config_from_json(reply_message_id)
+    config_data["img_missions"] = {}
+    for key in img_keys:
+        config_data["img_missions"][key] = "doing"
+    save_config_to_json(config_data, reply_message_id)
+
+    threads = []
+    for img_key in img_keys:
+        logger.info("开始furry处理线程: "+img_key)
+        with furry_lock:
+            # 在锁的上下文中启动线程,确保一次只启动一个
+            t = threading.Thread(target=img_furry, args=(reply_message_id, img_key))
+            threads.append(t)
+            t.start()
+            t.join()  # 等待当前线程完成再启动下一个
+
 # 开始换装的每个图片的线程
 def start_clothes_img_theards(reply_message_id):
     img_keys = get_img_key_from_input_images(reply_message_id)
@@ -122,11 +150,36 @@ def img_clothes(reply_message_id, img_key):
     else:
         mark_img_fail(reply_message_id, img_key)
 
+# 对单个图片做furry操作
+def img_furry(reply_message_id, img_key):
+    img_path = f"data/input_images/{reply_message_id}/{img_key}/{img_key}.jpg"
+    output_directory = f"data/input_images/{reply_message_id}/{img_key}"
+    # 开始furry
+    img_output_path = img_2_furry(get_prompt(reply_message_id), img_key, img_path, output_directory)
+    # 获取img失败则上传失败图片
+    if not img_output_path:
+        img_output_path = "asset/img_fail.png"
+        mark_img_fail(reply_message_id, img_key)
+    # 上传图片
+    output_img = get_img_file(img_output_path)
+    upload_output_img_res = upload_image(img_key, output_img, "output_img")
+    if upload_output_img_res:
+        mark_img_done(reply_message_id, img_key)
+        upload_output_img_key = upload_output_img_res.json()['data']['image_key']
+        # 更新card
+        upload_output_img_card ={
+            "msg_type": "interactive",
+            "content": json.dumps(update_process_card(reply_message_id, img_key, upload_output_img_key))
+        }
+        update_message(reply_message_id, upload_output_img_card)
+    else:
+        mark_img_fail(reply_message_id, img_key)
+
 
 # 配置prompt
 def get_prompt(reply_message_id):
     config = load_config_from_json(reply_message_id)
-    return "1girl, masterpiece, best quality, " + config['lora']
+    return "niji_style,1girl,simple background,furry," + config['animal'] + ",Disney style,movie lighting,vhibi,benhance,Octane Render,cute anime,chinoiserie painting,8K,3D,C4D,super details,best quality,"
 
 # 用户选择卡片
 def choose_updated_card(message_id, config_data):

+ 3 - 10
template/choose_card.json

@@ -37,13 +37,6 @@
                         "type": "action"
                     },
                     "options": [
-                        {
-                            "text": {
-                                "tag": "lark_md",
-                                "content": "试衣间"
-                            },
-                            "value": "clothes"
-                        },
                         {
                             "text": {
                                 "tag": "lark_md",
@@ -58,11 +51,11 @@
                     "tag": "select_static",
                     "placeholder": {
                         "tag": "plain_text",
-                        "content": "选择Lora人物"
+                        "content": "选择物"
                     },
                     "initial_option": "",
                     "value": {
-                        "type": "lora"
+                        "type": "animal"
                     },
                     "options": []
                 },
@@ -85,7 +78,7 @@
             "elements": [
                 {
                     "tag": "plain_text",
-                    "content": "图片需要有模特( •̀ ω •́ )✧"
+                    "content": "图片需要有人物"
                 }
             ]
         }

+ 2 - 9
template/no_img_card.json

@@ -17,26 +17,19 @@
         {
             "tag": "div",
             "text": {
-                "content": "- **试衣间功能**:开放\n- **Furry头像功能**:开放\n",
+                "content": "- **Furry头像功能**:开放\n",
                 "tag": "lark_md"
             }
         },
         {
             "tag": "hr"
         },
-        {
-            "tag": "div",
-            "text": {
-                "content": "[记录文档](https://hoxigames.feishu.cn/docx/HGKNdE9rKo7qeOxOGKgcGwgQnie)",
-                "tag": "lark_md"
-            }
-        },
         {
             "tag": "note",
             "elements": [
                 {
                     "tag": "lark_md",
-                    "content": "**v1.0.6 后续更新:试衣间可选保留背景、上传图片自动化训练Lora并添加至选项**"
+                    "content": "**v1.0.7 feat: Furry头像开放测试**"
                 }
             ]
         }

+ 6 - 9
template/start_card.json

@@ -40,13 +40,6 @@
                         "type": "action"
                     },
                     "options": [
-                        {
-                            "text": {
-                                "tag": "lark_md",
-                                "content": "试衣间"
-                            },
-                            "value": "clothes"
-                        },
                         {
                             "text": {
                                 "tag": "lark_md",
@@ -59,9 +52,13 @@
                 },
                 {
                     "tag": "select_static",
+                    "placeholder": {
+                        "tag": "plain_text",
+                        "content": "选择动物"
+                    },
                     "initial_option": "",
                     "value": {
-                        "type": "lora"
+                        "type": "animal"
                     },
                     "options": []
                 },
@@ -84,7 +81,7 @@
             "elements": [
                 {
                     "tag": "plain_text",
-                    "content": "图片需要有模特( •̀ ω •́ )✧"
+                    "content": "图片需要有模特"
                 }
             ]
         }

+ 4 - 10
template/upload_card.json

@@ -36,13 +36,6 @@
                         "type": "action"
                     },
                     "options": [
-                        {
-                            "text": {
-                                "tag": "lark_md",
-                                "content": "试衣间"
-                            },
-                            "value": "clothes"
-                        },
                         {
                             "text": {
                                 "tag": "lark_md",
@@ -57,10 +50,11 @@
                     "tag": "select_static",
                     "placeholder": {
                         "tag": "plain_text",
-                        "content": "选择Lora人物"
+                        "content": "选择物"
                     },
+                    "initial_option": "",
                     "value": {
-                        "type": "lora"
+                        "type": "animal"
                     },
                     "options": []
                 },
@@ -83,7 +77,7 @@
             "elements": [
                 {
                     "tag": "plain_text",
-                    "content": "图片需要有模特( •̀ ω •́ )✧"
+                    "content": "图片需要有模特"
                 }
             ]
         }

+ 8 - 1
utils/config_io.py

@@ -21,7 +21,7 @@ def load_config_from_json(message_id):
 
     # 检查文件是否存在,如果不存在则返回默认字典
     if not os.path.exists(json_path):
-        return {"user_message": "", "action": "", "lora": "", "if_download": False, "img_missions": {}}
+        return {"user_message": "", "action": "", "lora": "", "animal": "", "if_download": False, "img_missions": {}}
 
     # 从文件中读取JSON数据并转换为字典
     with open(json_path, 'r') as f:
@@ -78,6 +78,13 @@ def load_lora_options():
         lora_options = json.load(f)
     return lora_options
 
+# 从文件中读取animal选项
+def load_animal_options():
+    json_path = "animal_options.json"
+    with open(json_path, 'r', encoding='utf-8') as f:
+        animal_options = json.load(f)
+    return animal_options
+
 
 # 检测任务是否完成
 def if_mission_done(reply_message_id):

+ 2 - 2
utils/template_io.py

@@ -1,6 +1,6 @@
 import json
 import os
-from utils.config_io import if_mission_done, if_mission_fail, load_config_from_json, load_lora_options
+from utils.config_io import if_mission_done, if_mission_fail, load_config_from_json, load_lora_options, load_animal_options
 from utils.logger import logger
 
 
@@ -14,7 +14,7 @@ def load_template(template_name, reply_message_id = None):
             index = 3
         elif template_name == 'start_card':
             index = 4
-        lora_options= load_lora_options()
+        lora_options= load_animal_options()
         if reply_message_id:
             config_data = load_config_from_json(reply_message_id)
             template['elements'][index]['actions'][0]['initial_option'] = config_data['action']