rambo vor 1 Jahr
Ursprung
Commit
370ce25ceb

+ 2 - 1
.gitignore

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

+ 3 - 1
python/.gitignore

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

+ 23 - 3
python/api.py

@@ -1,11 +1,31 @@
-from middleware import *
+from models import *
 import datetime
-
+from services.SegmentService import SegmentService
+from services.deal_cutout import DealCutout
 
 @app.get("/")
-def index():
+async def index():
     """入口文件"""
     return {
         "message": "请求成功",
         "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
 from api import *
 import uvicorn
+import configparser, json
 
+# ===============默认数据配置=====================
+config = configparser.ConfigParser()
+config_name = "config.ini"
+config.read(config_name)
 # argparse
 parser = argparse.ArgumentParser(description="Process some integers.")
 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__":
     # 以api方式启动服务会出现警告,请忽略
     # 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有冲突,导致控制台无法显示信息。
     # 如果想要查看控制台输出,请单独启动服务 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.exceptions import RequestValidationError
 from fastapi.responses import JSONResponse
@@ -6,36 +6,42 @@ from fastapi.staticfiles import StaticFiles
 from fastapi.middleware.cors import CORSMiddleware
 import random
 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.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):
-    def __init__(self, msg: str,code:int=400):
+    def __init__(self, msg: str, code: int = -1):
         self.msg = msg
         self.code = code
 
 
 @app.exception_handler(UnicornException)
 async def error_throw(request: Request, exc: UnicornException):
+
     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 time
 import requests
-import settings
 import json
 import numpy as np
 import os, io
 from PIL import Image
 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):
     """Convert numpy classes to JSON serializable objects."""
 
@@ -27,25 +35,28 @@ class JsonEncoder(json.JSONEncoder):
 class GetOnlineData(object):
     def __init__(self):
         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):
-        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):
         # 获取抠图剩余次数
-        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)
         response_data = _s.json()
         return response_data["data"]
@@ -53,7 +64,8 @@ class GetOnlineData(object):
     def get_cutout_image_times(self):
         # 获取抠图剩余次数
         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)
         response_data = _s.json()
         if "data" not in response_data:
@@ -63,9 +75,7 @@ class GetOnlineData(object):
 
     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}
         _s = self.s.post(url=url, headers=self.post_headers, data=json.dumps(data), timeout=60)
         response_data = _s.json()
@@ -81,9 +91,7 @@ class GetOnlineData(object):
             return Image.open(BytesIO(pic))
 
     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}
         _s = self.s.post(url=url, headers=self.post_headers, data=json.dumps(data), timeout=30)
         response_data = _s.json()
@@ -100,9 +108,9 @@ class GetOnlineData(object):
             return _menu_dict
 
         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()
         try:
             menu_data = response_data["data"]["pc_menu"]
@@ -150,14 +158,13 @@ class GetOnlineData(object):
                   '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)
         # print(_s.text)
@@ -171,8 +178,7 @@ class GetOnlineData(object):
 
     def dispose_point(self,_type):
         # 扣分 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}
         _s = self.s.post(url=url, headers=self.post_headers,data=json.dumps(data), timeout=10)
         response_data = _s.json()
@@ -180,8 +186,7 @@ class GetOnlineData(object):
 
     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}
         _s = self.s.post(url=url, headers=self.post_headers,data=json.dumps(data), timeout=10)
         response_data = _s.json()

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

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

+ 1 - 7
python/services/remove_bg_pixian.py

@@ -1,7 +1,7 @@
 import copy
 import os
 from PIL import Image
-from other.remove_bg_ali import RemoveBgALi
+from .other.remove_bg_ali import RemoveBgALi
 import requests
 from io import BytesIO
 
@@ -222,9 +222,3 @@ class RemoveBgPiXian(object):
         with open(out_path, 'wb') as f:
             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": "./dist/",
         "excludes": ["*.txt"],
-        # "include_files": ["models/RMBG"],
+        "include_files": ["*.ini"],
         "optimize": 2,
     }
 }