rambo пре 1 година
родитељ
комит
370ce25ceb

+ 2 - 1
.gitignore

@@ -8,4 +8,5 @@ data/
 .vscode/launch.json
 .vscode/launch.json
 public/electron/
 public/electron/
 pnpm-lock.yaml
 pnpm-lock.yaml
-build/extraResources/py/*
+build/extraResources/py/*
+*.log

+ 3 - 1
python/.gitignore

@@ -1,4 +1,6 @@
 **/__pycache__
 **/__pycache__
 **.pyc
 **.pyc
 .venv/*
 .venv/*
-dist/*
+dist/*
+*.log
+log/*

+ 23 - 3
python/api.py

@@ -1,11 +1,31 @@
-from middleware import *
+from models import *
 import datetime
 import datetime
-
+from services.SegmentService import SegmentService
+from services.deal_cutout import DealCutout
 
 
 @app.get("/")
 @app.get("/")
-def index():
+async def index():
     """入口文件"""
     """入口文件"""
     return {
     return {
         "message": "请求成功",
         "message": "请求成功",
         "data": f"Hello, World!",
         "data": f"Hello, World!",
     }
     }
+
+
+@app.post("/api/check_select_images", description="检查目录或文件")
+async def checkSelect(params: CheckSelectImages):
+    service = SegmentService()
+    file_path = params.path
+    path_type = params.path_type
+    image_list = params.image_list
+    deal_cutout_mode = DealCutout()
+    if path_type == 0:
+        need_cutout_images = service.initImages(image_list=image_list)
+    else:
+        need_cutout_images = service.check_need_cutout_images(file_path)
+    if len([x for x in need_cutout_images if x["need_cutout"]]) == 0:
+        raise UnicornException("您所选文件夹下没有jpg图片,或对应图片已扣图")
+    deal_cutout_mode.need_cutout_images = need_cutout_images
+    deal_cutout_mode.start()
+    # print("list_result", need_cutout_images)
+    return success(need_cutout_images)

+ 4 - 0
python/config.ini

@@ -0,0 +1,4 @@
+[app]
+host=127.0.0.1
+port=7074
+debug=true

+ 15 - 2
python/main.py

@@ -3,7 +3,12 @@ import signal
 import sys
 import sys
 from api import *
 from api import *
 import uvicorn
 import uvicorn
+import configparser, json
 
 
+# ===============默认数据配置=====================
+config = configparser.ConfigParser()
+config_name = "config.ini"
+config.read(config_name)
 # argparse
 # argparse
 parser = argparse.ArgumentParser(description="Process some integers.")
 parser = argparse.ArgumentParser(description="Process some integers.")
 parser.add_argument("--port", type=int, default=7074, help="The port number.")
 parser.add_argument("--port", type=int, default=7074, help="The port number.")
@@ -24,7 +29,15 @@ signal.signal(signal.SIGINT, signal_handler)
 if __name__ == "__main__":
 if __name__ == "__main__":
     # 以api方式启动服务会出现警告,请忽略
     # 以api方式启动服务会出现警告,请忽略
     # app.run(port=args.port,use_reloader=True)
     # app.run(port=args.port,use_reloader=True)
-    uvicorn.run(app="main:app", host="127.0.0.1", port=int(args.port), reload=True)
+    host = config.get("app", "host")
+    port = config.get("app", "port")
+    debug = config.get("app", "debug")
+    uvicorn.run(
+        app="main:app",
+        host=host,
+        port=int(port),
+        reload=bool(debug),
+    )
     # 或许flask内置的stdio与node.js stdio有冲突,导致控制台无法显示信息。
     # 或许flask内置的stdio与node.js stdio有冲突,导致控制台无法显示信息。
     # 如果想要查看控制台输出,请单独启动服务 npm run dev-python
     # 如果想要查看控制台输出,请单独启动服务 npm run dev-python
-    print("python server is running at port:", args.port)
+    print("python server is running at port:", port)

+ 25 - 19
python/middleware.py

@@ -1,4 +1,4 @@
-from typing import Union,Optional
+from typing import Union, Optional
 from fastapi import FastAPI, Request, Body, Form, Query
 from fastapi import FastAPI, Request, Body, Form, Query
 from fastapi.exceptions import RequestValidationError
 from fastapi.exceptions import RequestValidationError
 from fastapi.responses import JSONResponse
 from fastapi.responses import JSONResponse
@@ -6,36 +6,42 @@ from fastapi.staticfiles import StaticFiles
 from fastapi.middleware.cors import CORSMiddleware
 from fastapi.middleware.cors import CORSMiddleware
 import random
 import random
 import os
 import os
-from pydantic import BaseModel, validator, conint, constr,Field
+from pydantic import BaseModel, validator, conint, constr, Field
+
 # 关闭文档
 # 关闭文档
-app = FastAPI( redoc_url=None)
+app = FastAPI(redoc_url=None)
 # app.mount("/model", StaticFiles(directory="model"), name="model")
 # app.mount("/model", StaticFiles(directory="model"), name="model")
 app.add_middleware(
 app.add_middleware(
-        CORSMiddleware,
-        # 允许跨域的源列表,例如 ["http://www.example.org"] 等等,["*"] 表示允许任何源
-        allow_origins=["*"],
-        # 跨域请求是否支持 cookie,默认是 False,如果为 True,allow_origins 必须为具体的源,不可以是 ["*"]
-        allow_credentials=False,
-        # 允许跨域请求的 HTTP 方法列表,默认是 ["GET"]
-        allow_methods=["*"],
-        # 允许跨域请求的 HTTP 请求头列表,默认是 [],可以使用 ["*"] 表示允许所有的请求头
-        # 当然 Accept、Accept-Language、Content-Language 以及 Content-Type 总之被允许的
-        allow_headers=["*"],
-        # 可以被浏览器访问的响应头, 默认是 [],一般很少指定
-        # expose_headers=["*"]
-        # 设定浏览器缓存 CORS 响应的最长时间,单位是秒。默认为 600,一般也很少指定
-        # max_age=1000
+    CORSMiddleware,
+    # 允许跨域的源列表,例如 ["http://www.example.org"] 等等,["*"] 表示允许任何源
+    allow_origins=["*"],
+    # 跨域请求是否支持 cookie,默认是 False,如果为 True,allow_origins 必须为具体的源,不可以是 ["*"]
+    allow_credentials=False,
+    # 允许跨域请求的 HTTP 方法列表,默认是 ["GET"]
+    allow_methods=["*"],
+    # 允许跨域请求的 HTTP 请求头列表,默认是 [],可以使用 ["*"] 表示允许所有的请求头
+    # 当然 Accept、Accept-Language、Content-Language 以及 Content-Type 总之被允许的
+    allow_headers=["*"],
+    # 可以被浏览器访问的响应头, 默认是 [],一般很少指定
+    # expose_headers=["*"]
+    # 设定浏览器缓存 CORS 响应的最长时间,单位是秒。默认为 600,一般也很少指定
+    # max_age=1000
 )
 )
 
 
 
 
 class UnicornException(Exception):
 class UnicornException(Exception):
-    def __init__(self, msg: str,code:int=400):
+    def __init__(self, msg: str, code: int = -1):
         self.msg = msg
         self.msg = msg
         self.code = code
         self.code = code
 
 
 
 
 @app.exception_handler(UnicornException)
 @app.exception_handler(UnicornException)
 async def error_throw(request: Request, exc: UnicornException):
 async def error_throw(request: Request, exc: UnicornException):
+
     return JSONResponse(
     return JSONResponse(
-        status_code=200, content={"code": exc.code, "error_msg": exc.msg}
+        status_code=200,
+        content={
+            "code": exc.code,
+            "msg": exc.msg,
+        },
     )
     )

+ 39 - 0
python/models.py

@@ -0,0 +1,39 @@
+from middleware import *
+
+
+def success(data):
+    """
+    成功
+    """
+    return {
+        "code": 0,
+        "msg": "请求成功",
+        "data": data,
+    }
+
+
+def failed(msg):
+    """
+    失败
+    """
+    return {
+        "code": -1,
+        "msg": msg,
+    }
+
+
+class CheckSelectImages(BaseModel):
+    # 检查目录
+    path_type: int = Field(default=0, description="地址类型;0图像;1目录")
+    path: str = Field(default=None, description="目录地址")
+    image_list: list[str] = Field(default=None, description="图像地址")
+
+
+class SegmentImages(BaseModel):
+    # 抠图
+    image_type: int = Field(default=0, description="图像类型;0非服装;1服装")
+    output_type: int = Field(default=0, description="图像类型;0仅移除背景;1白底图")
+    path_type: int = Field(default=0, description="地址类型;0图像;1目录")
+    segment_type: int = Field(default=0, description="抠图精细度;0普通;1精细")
+    path: int = Field(default=0, description="地址")
+    image_list: list[str] = Field(default=None, description="图像地址")

BIN
python/requirements.txt


+ 0 - 4
python/services/Exception.py

@@ -1,4 +0,0 @@
-class ApiException(Exception):
-    def __init__(self, msg: str,code:int=400):
-        self.msg = msg
-        self.code = code

+ 106 - 0
python/services/SegmentService.py

@@ -0,0 +1,106 @@
+import os
+from middleware import UnicornException
+
+
+class SegmentService:
+    is_fall_file = ["扣图", "已扣图"]
+    is_fall_dir = ["微信"]
+
+    def initImages(self, image_list):
+        need_cutout_images = []
+        for image_path in image_list:
+            root_path, file = os.path.split(image_path)
+            file_name, file_e = os.path.splitext(file)
+            need_cutout_images.append(
+                {
+                    "file_name": file_name,
+                    "file_e": file_e,
+                    "file_path": image_path,
+                    "file": file,
+                    "root_path": root_path,
+                    "need_cutout": True,
+                }
+            )
+
+        for index, image_data in enumerate(need_cutout_images):
+            root_path = image_data["root_path"]
+            file_name = image_data["file_name"]
+            _path_1 = "{}/{}.png".format(root_path, file_name)
+            _path_2 = "{}/已扣图/{}.png".format(root_path, file_name)
+            if os.path.exists(_path_1):
+                need_cutout_images[index]["need_cutout"] = False
+                continue
+            if os.path.exists(_path_2):
+                need_cutout_images[index]["need_cutout"] = False
+                continue
+        return need_cutout_images
+    def check_need_cutout_images(self, root_path):
+        _n_c = 0
+        need_cutout_images = []
+        _Type = [
+            ".jpg",
+            ".JPG",
+            ".jpeg",
+            ".JPEG",
+        ]
+        _is_cutout = []
+        if not os.path.isdir(root_path):
+            raise UnicornException("不是有效路径")
+        for file in os.listdir(root_path):
+            _n_c += 1
+            if _n_c > 500:
+                need_cutout_images = []
+                raise UnicornException("目录下文件过多,请检查目录是否正确")
+            file_path = "{}/{}".format(root_path, file)
+            if os.path.isdir(file_path):
+                print(file_path)
+                if file == "已扣图":
+                    # 哪些图片已经有抠图
+                    for x_file in os.listdir(file_path):
+                        x_file_name, x_file_e = os.path.splitext(x_file)
+                        if x_file_e == ".png":
+                            _is_cutout.append(x_file_name)
+
+        # ===============================================================
+        for file in os.listdir(root_path):
+            file_path = "{}/{}".format(root_path, file)
+            if os.path.isdir(file_path):
+                print(file_path)
+                # 不是已扣图文件夹,则进行遍历
+                f = True
+                for i in self.is_fall_dir:
+                    if i in file:
+                        f = False
+                        break
+                if f:
+                    self.check_need_cutout_images(file_path)
+
+            file_name, file_e = os.path.splitext(file)
+            if file_e not in _Type:
+                continue
+
+            # 检查文件是否在禁止抠图里
+            f = True
+            for i in self.is_fall_file:
+                if i in file:
+                    f = False
+                    break
+            if not f:
+                continue
+
+            need_cutout = False if file_name in _is_cutout else True
+            if os.path.exists("{}/{}.png".format(root_path, file_name)):
+                need_cutout = False
+
+            # 图片进行处理
+            need_cutout_images.append(
+                {
+                    "file_name": file_name,
+                    "file_e": file_e,
+                    "file_path": file_path,
+                    "file": file,
+                    "root_path": root_path,
+                    "need_cutout": need_cutout,
+                }
+            )
+        return need_cutout_images

+ 70 - 0
python/services/deal_cutout.py

@@ -0,0 +1,70 @@
+import time
+from concurrent.futures import as_completed, ThreadPoolExecutor, wait
+import threading
+from .remove_bg_pixian import RemoveBgPiXian
+from .other.module_online_data import GetOnlineData
+from .deal_one_image import DealOneImage, DealOneImageBeforehand
+from .other.log import MyLogger
+
+
+class DealCutout(threading.Thread):
+
+    def __init__(self):
+        super().__init__()
+        self.lock = threading.Lock()
+        self.need_cutout_images = {}
+        self.state = 2  # 1进行中 2停止
+        self.get_online_data = GetOnlineData()
+        self.is_upload_pic_num = 0
+        self.is_deal_num = 0
+        # 图片列表
+        self.upload_pic_dict = {}
+        self.logger = MyLogger().logger
+
+    def run(self):
+        self.get_online_data.refresh_headers()
+
+        executor = ThreadPoolExecutor(max_workers=4)
+        executor_pic_upload = ThreadPoolExecutor(max_workers=2)
+
+        tasks_1 = []
+        tasks_2 = []
+        self.state = 1
+        self.is_upload_pic_num = 0
+        self.is_deal_num = 0
+        num = 0
+        for image_data in self.need_cutout_images:
+            if not image_data["need_cutout"]:
+                continue
+            num += 1
+            task_1 = executor.submit(DealOneImage(image_data=image_data, lock=self.lock, windows=self, num=num).run)
+            tasks_1.append(task_1)
+
+            task_2 = executor_pic_upload.submit(
+                DealOneImageBeforehand(image_data=image_data, lock=self.lock, windows=self, num=num).run)
+            tasks_2.append(task_2)
+        self.check_thread(tasks_1, tasks_2)
+
+    def check_thread(self, *tasks_list):
+        time.sleep(2)
+        while 1:
+            f = True
+            for tasks in tasks_list:
+                done, not_done = wait(tasks)
+                if not_done:
+                    time.sleep(2)
+                    f = False
+                    continue
+
+                for task in done:
+                    try:
+                        result = task.result()
+                        # print(result)
+                    except BaseException as e:
+                        self.logger.info("有线程出错:{}".format(e))
+
+            if f:
+                break
+
+        # self.signal_data.emit({"_type": "complete",
+        #                        "data": ""})

+ 627 - 0
python/services/deal_one_image.py

@@ -0,0 +1,627 @@
+import copy
+import os.path
+from .other.module_online_data import GetOnlineData
+import time
+from .other.log import MyLogger
+import os
+from .remove_bg_pixian import RemoveBgPiXian
+from PIL import Image
+from .other.pic import Picture
+from .other.remove_bg_ali import RemoveBgALi
+import cv2
+import numpy as np
+from middleware import UnicornException
+
+
+class Base(object):
+
+    def __init__(self, image_data, lock, windows, num):
+        self.lock = lock
+        self.image_data = image_data
+        self.num = num
+        self.windows = windows
+        self.get_online_data = GetOnlineData()
+        self.file_path = image_data["file_path"]
+        self.file_name = image_data["file_name"]
+        self.file = os.path.split(self.file_path)[1]
+        self.is_once_data = {}
+        self.logger = MyLogger().logger
+
+    def add_log(self, text, _type="info"):
+        self.logger.info(
+            "第{}个,图片名称:{},内容:{}".format(self.num, self.file, text)
+        )
+
+    def show_image_info(self, data):
+        data["file_path"] = self.file_path
+        with self.lock:
+            data = {
+                "_type": "show_image_item_info",
+                "data": data,
+            }
+            # self.windows.signal_data.emit(data)
+
+    def send_info(
+        self,
+        text="",
+        is_success=None,
+        _type="show_text_browser",
+        need_point_return=False,
+    ):
+        with self.lock:
+            if is_success is not None:
+                if is_success:
+                    processing_failed = 0
+                    processing_successfully = 1
+                else:
+                    processing_failed = 1
+                    processing_successfully = 0
+                    # 分数返回
+                    if need_point_return:
+                        # 分数返回
+                        if self.is_once("add_point"):
+                            print(
+                                "第{}个,图片名称:{},内容:{}".format(
+                                    self.num, self.file, "扣分返回"
+                                )
+                            )
+                            self.dispose_point(_type="add")
+                            self.refresh_times(cumulative_frequency_times_change=-1)
+                        pass
+
+                # self.windows.signal_data.emit({"_type": "schedule",
+                #                                "data": {"processing_failed": processing_failed,
+                #                                         "processing_successfully": processing_successfully,
+                #                                         }})
+
+            if text:
+                data = {
+                    "_type": "show_text_browser",
+                    "data": text,
+                }
+                # self.windows.signal_data.emit(data)
+
+    def check_path(self, _path):
+        if not os.path.exists(_path):
+            os.mkdir(_path)
+        return True
+
+    def is_once(self, key):
+        if key not in self.is_once_data:
+            self.is_once_data[key] = 0
+            return True
+        return False
+
+    def refresh_times(self, cumulative_frequency_times_change=0):
+        data = {
+            "_type": "refresh_times",
+            "data": {
+                "cumulative_frequency_times_change": cumulative_frequency_times_change
+            },
+        }
+        # self.windows.signal_data.emit(data)
+        pass
+
+    def dispose_point(self, _type):
+        n = 3
+        while n:
+            n -= 1
+            try:
+                _r = self.get_online_data.dispose_point(_type)
+                balance = _r["data"]["balance"]
+                return True
+            except:
+                time.sleep(0.5)
+                continue
+
+        return False
+
+
+class DealOneImage(Base):
+    def __init__(self, image_data, lock, windows, num):
+        super().__init__(image_data, lock, windows, num)
+        self.image_data = image_data
+        self.lock = lock
+        self.windows = windows
+        self.num = num
+        self.file_path = image_data["file_path"]
+        self.file = os.path.split(self.file_path)[1]
+        self.root_path = image_data["root_path"]
+        self.r_pixian = RemoveBgPiXian()
+        self.file_name = image_data["file_name"]
+
+    def run(self):
+        # 直接调用抠图
+        # 1、增加获取key,2、key需要加密、3、429报错 重试再来拿一个KEY
+        self.add_log("开始处理")
+        self.show_image_info(
+            {
+                "text": "处理中",
+                "info": "",
+            }
+        )
+        remaining_times = self.get_online_data.get_cutout_image_times().get("balance")
+        print("remaining_times", remaining_times)
+        if remaining_times <= 0:
+            # self.send_info(text="次数不足,处理失败", is_success=False)
+            raise UnicornException("次数不足,处理失败")
+            return
+
+        # 检查图片上传是否有结束
+        n = 60
+        while 1:
+            if self.windows.state != 1:
+                # self.show_image_info({"text": "已取消", "info": "", })
+                # self.send_info(text="用户主动终止", is_success=False)
+                raise UnicornException("用户主动终止")
+                return
+            n -= 1
+            # print(n)
+            if self.file_path in self.windows.upload_pic_dict:
+                break
+
+            else:
+                time.sleep(1)
+                if n <= 0:
+                    text = "处理超时"
+                    self.send_info(text=text, is_success=False)
+
+                    _data = {
+                        "text": "出错/超时",
+                        "info": "处理超时",
+                    }
+                    self.show_image_info(_data)
+                    return
+                continue
+
+        s = time.time()
+        # print(self.upload_pic_dict[file_path])
+        _flag = self.windows.upload_pic_dict[self.file_path]["flag"]
+        if not _flag:
+            self.add_log("未查到上传的图片地址")
+            _data = {
+                "text": "出错/超时",
+                "info": "上传错误",
+            }
+            self.show_image_info(_data)
+            # self.send_info(text="上传错误", is_success=False)
+            raise UnicornException("上传错误")
+            return
+
+        image_deal_info = self.windows.upload_pic_dict[self.file_path][
+            "image_deal_info"
+        ]
+        original_im = self.windows.upload_pic_dict[self.file_path]["_im"]
+
+        self.show_image_info(
+            {
+                "text": "开始抠图",
+                "info": "",
+            }
+        )
+        self.add_log("抠图中")
+
+        with self.lock:
+            self.windows.is_upload_pic_num -= 1
+
+        # 抠图
+        out_root_path = "{}/已扣图".format(self.root_path)
+        self.check_path(out_root_path)
+
+        # 直接调用pixian
+        # second_cut_image,_ = self.r_pixian.run_by_image_url(image_url)
+
+        try:
+            balance = self.get_online_data.get_cutout_image_times()["balance"]
+            self.add_log("查询balance:{}成功".format(balance))
+            if balance <= 0:
+                self.add_log("次数不足,处理失败")
+                raise UnicornException("次数不足,处理失败")
+                self.send_info(text="次数不足,处理失败", is_success=False)
+                return
+        except:
+            self.add_log("查询balance失败")
+            raise UnicornException("查询balance失败")
+            self.send_info(text="查询balance失败", is_success=False)
+            return
+
+        n = 0
+        while 1:
+            # 获取key
+            if self.windows.state != 1:
+                self.show_image_info(
+                    {
+                        "text": "已取消",
+                        "info": "",
+                    }
+                )
+                raise UnicornException("用户主动终止")
+                # self.send_info(text="用户主动终止", is_success=False)
+                return
+            n += 1
+            data = self.get_online_data.get_key_secret()
+            key = (data["api_info"]["api_key"], data["api_info"]["api_serect"])
+            self.add_log("查询key成功")
+
+            if not key:
+                _data = {
+                    "text": "出错/超时",
+                    "info": "多次获取key失败",
+                }
+                self.add_log(text="多次获取key失败")
+                self.show_image_info(_data)
+                raise UnicornException("处理失败,请联系管理员")
+                self.send_info(text="处理失败,请联系管理员", is_success=False)
+                return
+
+            if self.is_once("sub_point"):
+                # 调用扣分
+                with self.lock:
+                    self.refresh_times(cumulative_frequency_times_change=1)
+                    f = self.dispose_point(_type="sub")
+                if not f:
+                    self.add_log(text="多次获取调用余额扣减失败")
+                    raise UnicornException("多次获取调用余额扣减失败")
+                    self.send_info(text="多次获取调用余额扣减失败", is_success=False)
+                    _data = {
+                        "text": "出错/超时",
+                        "info": "多次获取调用余额扣减失败",
+                    }
+                    self.show_image_info(_data)
+                    return
+
+            pixian_cutout_data = self.r_pixian.run_by_image_im(original_im, key)
+            # pixian_cutout_data = {"status_code":200}
+
+            if pixian_cutout_data["status_code"] == 200:
+                second_cut_image = pixian_cutout_data["im"]
+                # second_cut_image.save("xx.png")
+                # second_cut_image = Image.open("xx.png")
+                self.add_log(text="调用抠图完成")
+                break
+
+            elif pixian_cutout_data["status_code"] == 402:
+                if n >= 2:
+                    _data = {
+                        "text": "出错/超时",
+                        "info": "多次抠图失败:{}".format(
+                            pixian_cutout_data["status_code"]
+                        ),
+                    }
+                    self.add_log(
+                        text="多次抠图失败:{}".format(pixian_cutout_data["status_code"])
+                    )
+                    self.show_image_info(_data)
+                    self.send_info(
+                        text="处理失败,请联系管理员",
+                        is_success=False,
+                        need_point_return=True,
+                    )
+                    raise UnicornException("多次获取调用余额扣减失败")
+                    if self.is_once("余额不足报错"):
+                        # todo 余额不足报错,钉钉消息通知
+                        self.get_online_data.send_message(
+                            "Pixian:{} 余额不足".format(key)
+                        )
+                        pass
+                    return
+                self.add_log(
+                    text="抠图失败:{},延迟6秒".format(
+                        pixian_cutout_data["status_code"]
+                    )
+                )
+                time.sleep(6)
+                continue
+
+            elif pixian_cutout_data["status_code"] == 429:
+                if n >= 2:
+                    _data = {
+                        "text": "出错/超时",
+                        "info": "多次抠图失败:{}".format(
+                            pixian_cutout_data["status_code"]
+                        ),
+                    }
+
+                    self.show_image_info(_data)
+                    self.add_log(
+                        text="多次抠图失败:{}".format(pixian_cutout_data["status_code"])
+                    )
+                    self.send_info(
+                        text="处理失败,请联系管理员",
+                        is_success=False,
+                        need_point_return=True,
+                    )
+                    return
+
+                self.add_log(
+                    text="抠图失败:{},延迟10秒".format(
+                        pixian_cutout_data["status_code"]
+                    )
+                )
+                time.sleep(10)
+                continue
+            else:
+                _data = {
+                    "text": "出错/超时",
+                    "info": "抠图异常",
+                }
+                self.show_image_info(_data)
+                self.send_info(
+                    text="处理失败,请联系管理员",
+                    is_success=False,
+                    need_point_return=True,
+                )
+                if "message" in pixian_cutout_data:
+                    text = "抠图异常,code:{},message:{}".format(
+                        pixian_cutout_data["status_code"], pixian_cutout_data["message"]
+                    )
+                else:
+                    text = "抠图异常,code:{}".format(
+                        pixian_cutout_data["status_code"]
+                    )
+                self.add_log(text)
+                return
+
+        # 拼接处理
+        # print("耗时1:", time.time() - s)
+
+        try:
+            out_path = "{}/{}.png".format(out_root_path, self.file_name)
+            if image_deal_info["二次抠图是否缩放"]:
+                # print("图片尺寸还原")
+                self.add_log(text="图片尺寸进行还原")
+                original_im = image_deal_info["抠图扩边后PIL对象"]
+                second_cut_image = self.picture_resize_to_original(
+                    second_cut_image, original_im
+                )
+                # second_cut_image = second_cut_image.resize(image_deal_info["抠图扩边后图片大小"])
+            # 创建空白图片并粘贴回去
+            _img_im = Image.new(
+                mode="RGBA", size=image_deal_info["原始图片大小"], color=(0, 0, 0, 0)
+            )
+            _img_im.paste(
+                second_cut_image,
+                box=(
+                    image_deal_info["抠图扩边后位置"][0],
+                    image_deal_info["抠图扩边后位置"][1],
+                ),
+            )
+            _img_im.save(out_path)
+
+        except BaseException as e:
+            # print(e)
+            text = "{} 图片处理错误,代码44".format(e)
+            self.add_log(text)
+            self.send_info(text=text, is_success=False, need_point_return=True)
+            _data = {
+                "text": "出错/超时",
+                "info": text,
+            }
+            self.show_image_info(_data)
+            return
+
+        self.add_log(text="本张耗时:{}".format(time.time() - s))
+        self.send_info(text="抠图已完成", is_success=True)
+        self.show_image_info(
+            {
+                "text": "已完成",
+                "info": "",
+            }
+        )
+
+    def picture_resize_to_original(self, _img, original_im):
+        """
+
+        Parameters
+        ----------
+        _img 需要还原的PIL对象
+        original_im 原图对象
+
+        Returns
+        -------
+
+        """
+
+        # 将抠图结果转成mask
+        # 将抠图结果放大到原始图大小
+        _img = _img.resize(original_im.size)
+        new_big_mask = Image.new("RGB", _img.size, (0, 0, 0))
+        white = Image.new("RGB", _img.size, (255, 255, 255))
+        new_big_mask.paste(white, mask=_img.split()[3])
+
+        # ---------制作选区缩小的mask
+        mask = cv2.cvtColor(
+            np.asarray(new_big_mask), cv2.COLOR_BGR2GRAY
+        )  # 将PIL 格式转换为 CV对象
+        mask[mask != 255] = 0
+        # 黑白反转
+        # mask = 255 - mask
+        # 选区缩小10
+        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
+        erode_im = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
+
+        # -------再进行抠图处理
+        mask = Image.fromarray(
+            cv2.cvtColor(erode_im, cv2.COLOR_GRAY2RGBA)
+        )  # CV 对象转 PIL
+        transparent_im = Image.new("RGBA", original_im.size, (0, 0, 0, 0))
+        transparent_im.paste(original_im, (0, 0), mask.convert("L"))
+        # 上述抠图结果进行拼接
+        _img.paste(transparent_im, (0, 0), transparent_im)
+
+        return _img
+
+
+class DealOneImageBeforehand(Base):
+    def __init__(self, image_data, lock, windows, num):
+        super().__init__(image_data, lock, windows, num)
+        self.image_data = image_data
+        self.lock = lock
+        self.windows = windows
+        self.get_online_data = GetOnlineData()
+        self.num = num
+        self.file_path = image_data["file_path"]
+        self.file = os.path.split(self.file_path)[1]
+        self.root_path = image_data["root_path"]
+        self.r_ali = RemoveBgALi()
+        self.file_name = image_data["file_name"]
+
+    def run(self):
+        # 增加阿里调用报错的重试机制
+        # a = 1/0
+        # raise "11111111111111111"
+        while 1:
+            if self.windows.state != 1:
+                self.show_image_info(
+                    {
+                        "text": "已取消",
+                        "info": "",
+                    }
+                )
+                return
+            with self.lock:
+                if self.windows.is_upload_pic_num >= 4:
+                    f = False
+                else:
+                    self.windows.is_upload_pic_num += 1
+                    f = True
+            if f:
+                break
+            else:
+                time.sleep(1)
+                continue
+        image_deal_info = {}
+        try:
+            _data = {
+                "text": "开始上传",
+                "info": "",
+            }
+            self.show_image_info(_data)
+            cut_image, image_deal_info = self.get_image_cut()
+
+            f = True
+            url = ""
+            self.show_image_info(
+                {
+                    "text": "上传成功",
+                    "info": "",
+                }
+            )
+        except BaseException as e:
+            # print(e)
+            self.show_image_info(
+                {
+                    "text": "上传出错",
+                    "info": "{}".format(e),
+                }
+            )
+            f = False
+            url = ""
+            cut_image = ""
+
+        # cut_image.save("1.jpg",format="JPEG")
+        # raise 1
+
+        with self.lock:
+            self.windows.upload_pic_dict[self.file_path] = {
+                "url": url,
+                "image_deal_info": image_deal_info,
+                "flag": f,
+                "_im": cut_image,
+            }
+
+    def get_image_cut(self):
+        original_pic = Picture(self.file_path)
+        original_pic.im = self.get_image_orientation(original_pic.im)
+        original_pic.x, original_pic.y = original_pic.im.size
+
+        original_pic.im = original_pic.im.convert("RGB")
+        image_deal_info = {}
+        image_deal_info["原始图片大小"] = (original_pic.x, original_pic.y)
+        if original_pic.x * original_pic.y < 1000000:
+            cut_image = original_pic.im
+            image_deal_info["抠图扩边后图片大小"] = cut_image.size
+            image_deal_info["二次抠图是否缩放"] = False
+            image_deal_info["抠图扩边后位置"] = (0, 0, original_pic.x, original_pic.y)
+        else:
+            self.add_log("开始预抠图处理")
+            cut_image = self.r_ali.get_image_cut(
+                file_path=None, out_file_path=None, original_im=original_pic.im
+            )
+            # cut_image.save("XX1.png")
+
+            self.add_log("预抠图处理结束")
+
+            x1, y1, x2, y2 = cut_image.getbbox()
+            image_deal_info["鞋子原始位置"] = (x1, y1, x2, y2)
+            o_w, o_h = cut_image.size
+            image_deal_info["鞋子原始抠图后大小"] = (o_w, o_h)
+            # 扩边处理
+            _w, _h = x2 - x1, y2 - y1
+            out_px = 0.025
+            _w, _h = int(out_px * _w), int(out_px * _h)
+            n_x1, n_y1, n_x2, n_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h
+            if n_x1 < 0:
+                n_x1 = 0
+            if n_y1 < 0:
+                n_y1 = 0
+            if n_x2 > o_w:
+                n_x2 = o_w
+            if n_y2 > o_h:
+                n_y2 = o_h
+            image_deal_info["抠图扩边后位置"] = (n_x1, n_y1, n_x2, n_y2)
+            cut_image = original_pic.im.crop(image_deal_info["抠图扩边后位置"])
+
+            image_deal_info["抠图扩边后图片大小"] = cut_image.size
+            x, y = image_deal_info["抠图扩边后图片大小"]
+            # max_size = 32000000
+            max_size = 12000000
+            if x * y > max_size:
+                r = max_size / (x * y)
+                size = (int(x * r), int(y * r))
+                self.add_log(
+                    text="图片进行压缩,压缩前:{},压缩后:{}".format(
+                        image_deal_info["抠图扩边后图片大小"], size
+                    )
+                )
+                image_deal_info["抠图扩边后PIL对象"] = copy.deepcopy(cut_image)
+                cut_image = cut_image.resize(size=size)
+                # print(cut_image.size)
+                # print(image_deal_info["抠图扩边后PIL对象"].size)
+                image_deal_info["二次抠图是否缩放"] = True
+            else:
+                image_deal_info["二次抠图是否缩放"] = False
+        return cut_image, image_deal_info
+
+    def get_image_orientation(self, img):
+        # 获取EXIF数据
+        exif = img._getexif()
+        if exif is not None:
+            # EXIF标签274对应的是Orientation
+            orientation = exif.get(0x0112)
+            if orientation == 2:
+                # 水平翻转
+                img = img.transpose(Image.FLIP_LEFT_RIGHT)
+            elif orientation == 3:
+                # 旋转180度
+                img = img.rotate(180, expand=True)
+            elif orientation == 4:
+                # 垂直翻转
+                img = img.transpose(Image.FLIP_TOP_BOTTOM)
+            elif orientation == 5:
+                # 水平翻转后顺时针旋转90度
+                img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
+            elif orientation == 6:
+                # 顺时针旋转90度
+                img = img.transpose(Image.ROTATE_270)
+            elif orientation == 7:
+                # 水平翻转后逆时针旋转90度
+                img = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
+            elif orientation == 8:
+                # 逆时针旋转90度
+                img = img.transpose(Image.ROTATE_90)
+        else:
+            print("没有EXIF数据或没有方向信息")
+            orientation = 1
+
+        return img

+ 42 - 37
python/services/other/module_online_data.py

@@ -4,14 +4,22 @@ import copy
 import random
 import random
 import time
 import time
 import requests
 import requests
-import settings
 import json
 import json
 import numpy as np
 import numpy as np
 import os, io
 import os, io
 from PIL import Image
 from PIL import Image
 from io import BytesIO
 from io import BytesIO
-
-
+Authorization = "Bearer e4ffe8dfe772670ca9c9d234104a7be67a31d689"
+Origin = "http://my2.pubdata.cn"
+Host = "mybackend2.pubdata.cn"
+DOMAIN = "http://mybackend2.pubdata.cn"
+Headers = {
+    "Authorization": Authorization,
+    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
+    "Origin": Origin,
+    "Host": Host,
+    "Content-Type": "application/json;charset=UTF-8",
+}
 class JsonEncoder(json.JSONEncoder):
 class JsonEncoder(json.JSONEncoder):
     """Convert numpy classes to JSON serializable objects."""
     """Convert numpy classes to JSON serializable objects."""
 
 
@@ -27,25 +35,28 @@ class JsonEncoder(json.JSONEncoder):
 class GetOnlineData(object):
 class GetOnlineData(object):
     def __init__(self):
     def __init__(self):
         self.s = requests.session()
         self.s = requests.session()
-        self.post_headers = {"Authorization": settings.Headers["Authorization"],
-                             "Origin": settings.Headers["Origin"],
-                             "Host": settings.Headers["Host"],
-                             "Content-Length": "0",
-                             "Content-Type": "application/json",
-                             "Accept": "application/json"}
+        self.post_headers = {
+            "Authorization": Authorization,
+            "Origin": Origin,
+            "Host": Host,
+            "Content-Length": "0",
+            "Content-Type": "application/json",
+            "Accept": "application/json",
+        }
 
 
     def refresh_headers(self):
     def refresh_headers(self):
-        self.post_headers = {"Authorization": settings.Headers["Authorization"],
-                             "Origin": settings.Headers["Origin"],
-                             "Host": settings.Headers["Host"],
-                             "Content-Length": "0",
-                             "Content-Type": "application/json",
-                             "Accept": "application/json"}
+        self.post_headers = {
+            "Authorization": Authorization,
+            "Origin": Origin,
+            "Host": Host,
+            "Content-Length": "0",
+            "Content-Type": "application/json",
+            "Accept": "application/json",
+        }
 
 
     def get_key_secret(self):
     def get_key_secret(self):
         # 获取抠图剩余次数
         # 获取抠图剩余次数
-        url = "{domain}/api/ai_image/client/get_key_serect".format(
-            domain=settings.DOMAIN)
+        url = "{domain}/api/ai_image/client/get_key_serect".format(domain=DOMAIN)
         _s = self.s.post(url=url, headers=self.post_headers, timeout=10)
         _s = self.s.post(url=url, headers=self.post_headers, timeout=10)
         response_data = _s.json()
         response_data = _s.json()
         return response_data["data"]
         return response_data["data"]
@@ -53,7 +64,8 @@ class GetOnlineData(object):
     def get_cutout_image_times(self):
     def get_cutout_image_times(self):
         # 获取抠图剩余次数
         # 获取抠图剩余次数
         url = "{domain}/api/ai_image/client/search_company_balance".format(
         url = "{domain}/api/ai_image/client/search_company_balance".format(
-            domain=settings.DOMAIN)
+            domain=DOMAIN
+        )
         _s = self.s.post(url=url, headers=self.post_headers, timeout=10)
         _s = self.s.post(url=url, headers=self.post_headers, timeout=10)
         response_data = _s.json()
         response_data = _s.json()
         if "data" not in response_data:
         if "data" not in response_data:
@@ -63,9 +75,7 @@ class GetOnlineData(object):
 
 
     def search_progress(self, generate_ids):
     def search_progress(self, generate_ids):
         # 查进度
         # 查进度
-        url = "{domain}/api/ai_image/client/search_progress".format(
-            domain=settings.DOMAIN
-        )
+        url = "{domain}/api/ai_image/client/search_progress".format(domain=DOMAIN)
         data = {"generate_ids": generate_ids}
         data = {"generate_ids": generate_ids}
         _s = self.s.post(url=url, headers=self.post_headers, data=json.dumps(data), timeout=60)
         _s = self.s.post(url=url, headers=self.post_headers, data=json.dumps(data), timeout=60)
         response_data = _s.json()
         response_data = _s.json()
@@ -81,9 +91,7 @@ class GetOnlineData(object):
             return Image.open(BytesIO(pic))
             return Image.open(BytesIO(pic))
 
 
     def remove_background(self, images_url):
     def remove_background(self, images_url):
-        url = "{domain}/api/ai_image/client/remove_background".format(
-            domain=settings.DOMAIN
-        )
+        url = "{domain}/api/ai_image/client/remove_background".format(domain=DOMAIN)
         data = {"images": images_url}
         data = {"images": images_url}
         _s = self.s.post(url=url, headers=self.post_headers, data=json.dumps(data), timeout=30)
         _s = self.s.post(url=url, headers=self.post_headers, data=json.dumps(data), timeout=30)
         response_data = _s.json()
         response_data = _s.json()
@@ -100,9 +108,9 @@ class GetOnlineData(object):
             return _menu_dict
             return _menu_dict
 
 
         url = "{domain}/api/backend/basic/get_current_menu".format(
         url = "{domain}/api/backend/basic/get_current_menu".format(
-            domain=settings.DOMAIN,
+            domain=DOMAIN,
         )
         )
-        _s = self.s.get(url=url, headers=settings.Headers)
+        _s = self.s.get(url=url, headers=Headers)
         response_data = _s.json()
         response_data = _s.json()
         try:
         try:
             menu_data = response_data["data"]["pc_menu"]
             menu_data = response_data["data"]["pc_menu"]
@@ -150,14 +158,13 @@ class GetOnlineData(object):
                   'image/{}'.format("JPEG")))
                   'image/{}'.format("JPEG")))
             ]
             ]
 
 
-        url = "{domain}/api/backend/upload".format(
-            domain=settings.DOMAIN
-        )
+        url = "{domain}/api/backend/upload".format(domain=DOMAIN)
 
 
-        headers = {"Authorization": settings.Headers["Authorization"],
-                   "Origin": settings.Headers["Origin"],
-                   "Host": settings.Headers["Host"],
-                   }
+        headers = {
+            "Authorization": Authorization,
+            "Origin": Origin,
+            "Host": Host,
+        }
 
 
         _s = requests.post(url=url, headers=headers, files=files, timeout=60)
         _s = requests.post(url=url, headers=headers, files=files, timeout=60)
         # print(_s.text)
         # print(_s.text)
@@ -171,8 +178,7 @@ class GetOnlineData(object):
 
 
     def dispose_point(self,_type):
     def dispose_point(self,_type):
         # 扣分 sub;add为增加分数,每次操作一分
         # 扣分 sub;add为增加分数,每次操作一分
-        url = "{domain}/api/ai_image/client/dispose_point".format(
-            domain=settings.DOMAIN)
+        url = "{domain}/api/ai_image/client/dispose_point".format(domain=DOMAIN)
         data = {"type": _type}
         data = {"type": _type}
         _s = self.s.post(url=url, headers=self.post_headers,data=json.dumps(data), timeout=10)
         _s = self.s.post(url=url, headers=self.post_headers,data=json.dumps(data), timeout=10)
         response_data = _s.json()
         response_data = _s.json()
@@ -180,8 +186,7 @@ class GetOnlineData(object):
 
 
     def send_message(self,text):
     def send_message(self,text):
         # 发送钉钉消息
         # 发送钉钉消息
-        url = "{domain}/api/ai_image/client/send_message".format(
-            domain=settings.DOMAIN)
+        url = "{domain}/api/ai_image/client/send_message".format(domain=DOMAIN)
         data = {"message": text}
         data = {"message": text}
         _s = self.s.post(url=url, headers=self.post_headers,data=json.dumps(data), timeout=10)
         _s = self.s.post(url=url, headers=self.post_headers,data=json.dumps(data), timeout=10)
         response_data = _s.json()
         response_data = _s.json()

+ 1 - 1
python/services/other/remove_bg_ali.py

@@ -13,7 +13,7 @@ import requests
 from io import BytesIO
 from io import BytesIO
 import cv2
 import cv2
 import numpy as np
 import numpy as np
-from other.module_online_data import GetOnlineData
+from .module_online_data import GetOnlineData
 
 
 
 
 # todo 获取密钥
 # todo 获取密钥

+ 1 - 7
python/services/remove_bg_pixian.py

@@ -1,7 +1,7 @@
 import copy
 import copy
 import os
 import os
 from PIL import Image
 from PIL import Image
-from other.remove_bg_ali import RemoveBgALi
+from .other.remove_bg_ali import RemoveBgALi
 import requests
 import requests
 from io import BytesIO
 from io import BytesIO
 
 
@@ -222,9 +222,3 @@ class RemoveBgPiXian(object):
         with open(out_path, 'wb') as f:
         with open(out_path, 'wb') as f:
             f.write(pic)
             f.write(pic)
 
 
-
-if __name__ == '__main__':
-    r = RemoveBgPiXian()
-    path = r"C:\Users\gymmc\Desktop\白底部分品类45度\测试\eva_keai_1.png"
-    out_path = "{}._no_bg-out.png".format(path)
-    r.get_image_cut(path, out_file_path=out_path)

+ 1 - 1
python/setup.py

@@ -11,7 +11,7 @@ options = {
     "build_exe": {
     "build_exe": {
         "build_exe": "./dist/",
         "build_exe": "./dist/",
         "excludes": ["*.txt"],
         "excludes": ["*.txt"],
-        # "include_files": ["models/RMBG"],
+        "include_files": ["*.ini"],
         "optimize": 2,
         "optimize": 2,
     }
     }
 }
 }