Spaces:
Running
Running
# -*- coding: utf-8 -*- | |
""" | |
@Time : 2022/8/27 14:17 | |
@Author : cuny | |
@File : app.py | |
@Software : PyCharm | |
@Introduce: | |
查看包版本等一系列操作 | |
""" | |
import os | |
import sys | |
import json | |
import shutil | |
import zipfile | |
import requests | |
from argparse import ArgumentParser | |
from importlib.metadata import version | |
try: # 加上这个try的原因在于本地环境和云函数端的import形式有所不同 | |
from qcloud_cos import CosConfig | |
from qcloud_cos import CosS3Client | |
except ImportError: | |
try: | |
from qcloud_cos_v5 import CosConfig | |
from qcloud_cos_v5 import CosS3Client | |
from qcloud_cos.cos_exception import CosServiceError | |
except ImportError: | |
raise ImportError("请下载腾讯云COS相关代码包:pip install cos-python-sdk-v5") | |
class HivisionaiParams(object): | |
""" | |
定义一些基本常量 | |
""" | |
# 文件所在路径 | |
# 包名称 | |
package_name = "HY-sdk" | |
# 腾讯云相关变量 | |
region = "ap-beijing" | |
zip_key = "HY-sdk/" # zip存储的云端文件夹路径,这里改了publish.yml也需要更改 | |
# 云端用户配置,如果在cloud_config_save不存在,就需要下载此文件 | |
user_url = "https://hy-sdk-config-1305323352.cos.ap-beijing.myqcloud.com/sdk-user/user_config.json" | |
bucket = "cloud-public-static-1306602019" | |
# 压缩包类型 | |
file_format = ".zip" | |
# 下载路径(.hivisionai文件夹路径) | |
download_path = os.path.expandvars('$HOME') | |
# zip文件、zip解压缩文件的存放路径 | |
save_folder = f"{os.path.expandvars('$HOME')}/.hivisionai/sdk" | |
# 腾讯云配置文件存放路径 | |
cloud_config_save = f"{os.path.expandvars('$HOME')}/.hivisionai/user_config.json" | |
# 项目路径 | |
hivisionai_path = os.path.dirname(os.path.dirname(__file__)) | |
# 使用hivisionai的路径 | |
getcwd = os.getcwd() | |
# HY-func的依赖配置 | |
# 每个依赖会包含三个参数,保存路径(save_path,相对于HY_func的路径)、下载url(url) | |
functionDependence = { | |
"configs": [ | |
# --------- 配置文件部分 | |
# _lib | |
{ | |
"url": "https://hy-sdk-config-1305323352.cos.ap-beijing.myqcloud.com/hy-func/_lib/config/aliyun-human-matting-api.json", | |
"save_path": "_lib/config/aliyun-human-matting-api.json" | |
}, | |
{ | |
"url": "https://hy-sdk-config-1305323352.cos.ap-beijing.myqcloud.com/hy-func/_lib/config/megvii-face-plus-api.json", | |
"save_path": "_lib/config/megvii-face-plus-api.json" | |
}, | |
{ | |
"url": "https://hy-sdk-config-1305323352.cos.ap-beijing.myqcloud.com/hy-func/_lib/config/volcano-face-change-api.json", | |
"save_path": "_lib/config/volcano-face-change-api.json" | |
}, | |
# _service | |
{ | |
"url": "https://hy-sdk-config-1305323352.cos.ap-beijing.myqcloud.com/hy-func/_service/config/func_error_conf.json", | |
"save_path": "_service/utils/config/func_error_conf.json" | |
}, | |
{ | |
"url": "https://hy-sdk-config-1305323352.cos.ap-beijing.myqcloud.com/hy-func/_service/config/service_config.json", | |
"save_path": "_service/utils/config/service_config.json" | |
}, | |
# --------- 模型部分 | |
# 模型部分存储在Notion文档当中 | |
# https://www.notion.so/HY-func-cc6cc41ba6e94b36b8fa5f5d67d1683f | |
], | |
"weights": "https://www.notion.so/HY-func-cc6cc41ba6e94b36b8fa5f5d67d1683f" | |
} | |
class HivisionaiUtils(object): | |
""" | |
本类为一些基本工具类,包含代码复用相关内容 | |
""" | |
def get_client(): | |
"""获取cos客户端对象""" | |
def get_secret(): | |
# 首先判断cloud_config_save下是否存在 | |
if not os.path.exists(HivisionaiParams.cloud_config_save): | |
print("Downloading user_config...") | |
resp = requests.get(HivisionaiParams.user_url) | |
open(HivisionaiParams.cloud_config_save, "wb").write(resp.content) | |
config = json.load(open(HivisionaiParams.cloud_config_save, "r")) | |
return config["secret_id"], config["secret_key"] | |
# todo 接入HY-Auth-Sync | |
secret_id, secret_key = get_secret() | |
return CosS3Client(CosConfig(Region=HivisionaiParams.region, Secret_id=secret_id, Secret_key=secret_key)) | |
def get_all_versions(self): | |
"""获取云端的所有版本号""" | |
def getAllVersion_base(): | |
""" | |
返回cos存储桶内部的某个文件夹的内部名称 | |
ps:如果需要修改默认的存储桶配置,请在代码运行的时候加入代码 s.bucket = 存储桶名称 (s是对象实例) | |
返回的内容存储在response["Content"],不过返回的数据大小是有限制的,具体内容还是请看官方文档。 | |
Returns: | |
[版本列表] | |
""" | |
resp = client.list_objects( | |
Bucket=HivisionaiParams.bucket, | |
Prefix=HivisionaiParams.zip_key, | |
Marker=marker | |
) | |
versions_list.extend([x["Key"].split("/")[-1].split(HivisionaiParams.file_format)[0] for x in resp["Contents"] if int(x["Size"]) > 0]) | |
if resp['IsTruncated'] == 'false': # 接下来没有数据了,就退出 | |
return "" | |
else: | |
return resp['NextMarker'] | |
client = self.get_client() | |
marker = "" | |
versions_list = [] | |
while True: # 轮询 | |
try: | |
marker = getAllVersion_base() | |
except KeyError as e: | |
print(e) | |
raise | |
if len(marker) == 0: # 没有数据了 | |
break | |
return versions_list | |
def get_newest_version(self): | |
"""获取最新的版本号""" | |
versions_list = self.get_all_versions() | |
# reverse=True,降序 | |
versions_list.sort(key=lambda x: int(x.split(".")[-1]), reverse=True) | |
versions_list.sort(key=lambda x: int(x.split(".")[-2]), reverse=True) | |
versions_list.sort(key=lambda x: int(x.split(".")[-3]), reverse=True) | |
return versions_list[0] | |
def download_version(self, v): | |
""" | |
在存储桶中下载文件,将下载好的文件解压至本地 | |
Args: | |
v: 版本号,x.x.x | |
Returns: | |
None | |
""" | |
file_name = v + HivisionaiParams.file_format | |
client = self.get_client() | |
print(f"Download to {HivisionaiParams.save_folder}...") | |
try: | |
resp = client.get_object(HivisionaiParams.bucket, HivisionaiParams.zip_key + "/" + file_name) | |
contents = resp["Body"].get_raw_stream().read() | |
except CosServiceError: | |
print(f"[{file_name}.zip] does not exist, please check your version!") | |
sys.exit() | |
if not os.path.exists(HivisionaiParams.save_folder): | |
os.makedirs(HivisionaiParams.save_folder) | |
open(os.path.join(HivisionaiParams.save_folder, file_name), "wb").write(contents) | |
print("Download success!") | |
def download_dependence(path=None): | |
""" | |
一键下载HY-sdk所需要的所有依赖,需要注意的是,本方法必须在运行pip install之后使用(运行完pip install之后才会出现hivisionai文件夹) | |
Args: | |
path: 文件路径,精确到hivisionai文件夹的上一个目录,如果为None,则默认下载到python环境下hivisionai安装的目录 | |
Returns: | |
下载相应内容到指定位置 | |
""" | |
# print("指定的下载路径:", path) # 此时在path路径下必然存在一个hivisionai文件夹 | |
# print("系统安装的hivisionai库的路径:", HivisionaiParams.hivisionai_path) | |
print("Dependence downloading...") | |
if path is None: | |
path = HivisionaiParams.hivisionai_path | |
# ----------------下载mtcnn模型文件 | |
mtcnn_path = os.path.join(path, "hivisionai/hycv/mtcnn_onnx/weights") | |
base_url = "https://linimages.oss-cn-beijing.aliyuncs.com/" | |
onnx_files = ["pnet.onnx", "rnet.onnx", "onet.onnx"] | |
print(f"Downloading mtcnn model in {mtcnn_path}") | |
if not os.path.exists(mtcnn_path): | |
os.mkdir(mtcnn_path) | |
for onnx_file in onnx_files: | |
if not os.path.exists(os.path.join(mtcnn_path, onnx_file)): | |
# download onnx model | |
onnx_url = base_url + onnx_file | |
print("Downloading Onnx Model in:", onnx_url) | |
r = requests.get(onnx_url, stream=True) | |
if r.status_code == 200: | |
open(os.path.join(mtcnn_path, onnx_file), 'wb').write(r.content) # 将内容写入文件 | |
print(f"Download finished -- {onnx_file}") | |
del r | |
# ---------------- | |
print("Dependence download finished...") | |
class HivisionaiApps(object): | |
""" | |
本类为app对外暴露的接口,为了代码规整性,这里使用类来对暴露接口进行调整 | |
""" | |
def show_cloud_version(): | |
"""查看在cos中的所有HY-sdk版本""" | |
print("Connect to COS...") | |
versions_list = hivisionai_utils.get_all_versions() | |
# reverse=True,降序 | |
versions_list.sort(key=lambda x: int(x.split(".")[-1]), reverse=True) | |
versions_list.sort(key=lambda x: int(x.split(".")[-2]), reverse=True) | |
versions_list.sort(key=lambda x: int(x.split(".")[-3]), reverse=True) | |
if len(versions_list) == 0: | |
print("There is no version currently, please release it first!") | |
sys.exit() | |
versions = "The currently existing versions (Keep 10): \n" | |
for i, v in enumerate(versions_list): | |
versions += str(v) + " " | |
if i == 9: | |
break | |
print(versions) | |
def upgrade(v: str, enforce: bool = False, save_cached: bool = False): | |
""" | |
自动升级HY-sdk到指定版本 | |
Args: | |
v: 指定的版本号,格式为x.x.x | |
enforce: 是否需要强制执行更新命令 | |
save_cached: 是否保存下载的wheel文件,默认为否 | |
Returns: | |
None | |
""" | |
def check_format(): | |
# noinspection PyBroadException | |
try: | |
major, minor, patch = v.split(".") | |
int(major) | |
int(minor) | |
int(patch) | |
except Exception as e: | |
print(f"Illegal version number!\n{e}") | |
pass | |
print("Upgrading, please wait a moment...") | |
if v == "-1": | |
v = hivisionai_utils.get_newest_version() | |
# 检查format的格式 | |
check_format() | |
if v == version(HivisionaiParams.package_name) and not enforce: | |
print(f"Current version: {v} already exists, skip installation.") | |
sys.exit() | |
hivisionai_utils.download_version(v) | |
# 下载完毕(下载至save_folder),解压文件 | |
target_zip = os.path.join(HivisionaiParams.save_folder, f"{v}.zip") | |
assert zipfile.is_zipfile(target_zip), "Decompression failed, and the target was not a zip file." | |
new_dir = target_zip.replace('.zip', '') # 解压的文件名 | |
if os.path.exists(new_dir): # 判断文件夹是否存在 | |
shutil.rmtree(new_dir) | |
os.mkdir(new_dir) # 新建文件夹 | |
f = zipfile.ZipFile(target_zip) | |
f.extractall(new_dir) # 提取zip文件 | |
print("Decompressed, begin to install...") | |
os.system(f'pip3 install {os.path.join(new_dir, "**.whl")}') | |
# 开始自动下载必要的模型依赖 | |
hivisionai_utils.download_dependence() | |
# 安装完毕,如果save_cached为真,删除"$HOME/.hivisionai/sdk"内部的所有文件元素 | |
if save_cached is True: | |
os.system(f'rm -rf {HivisionaiParams.save_folder}/**') | |
def export(path): | |
""" | |
输出最新版本的文件到命令运行的path目录 | |
Args: | |
path: 用户输入的路径 | |
Returns: | |
输出最新的hivisionai到path目录 | |
""" | |
# print(f"当前路径: {os.path.join(HivisionaiParams.getcwd, path)}") | |
# print(f"文件路径: {os.path.dirname(__file__)}") | |
export_path = os.path.join(HivisionaiParams.getcwd, path) | |
# 判断输出路径存不存在,如果不存在,就报错 | |
assert os.path.exists(export_path), f"{export_path} dose not Exists!" | |
v = hivisionai_utils.get_newest_version() | |
# 下载文件到.hivisionai/sdk当中 | |
hivisionai_utils.download_version(v) | |
# 下载完毕(下载至save_folder),解压文件 | |
target_zip = os.path.join(HivisionaiParams.save_folder, f"{v}.zip") | |
assert zipfile.is_zipfile(target_zip), "Decompression failed, and the target was not a zip file." | |
new_dir = os.path.basename(target_zip.replace('.zip', '')) # 解压的文件名 | |
new_dir = os.path.join(export_path, new_dir) # 解压的文件路径 | |
if os.path.exists(new_dir): # 判断文件夹是否存在 | |
shutil.rmtree(new_dir) | |
os.mkdir(new_dir) # 新建文件夹 | |
f = zipfile.ZipFile(target_zip) | |
f.extractall(new_dir) # 提取zip文件 | |
print("Decompressed, begin to export...") | |
# 强制删除bin/hivisionai和hivisionai/以及HY_sdk-** | |
bin_path = os.path.join(export_path, "bin") | |
hivisionai_path = os.path.join(export_path, "hivisionai") | |
sdk_path = os.path.join(export_path, "HY_sdk-**") | |
os.system(f"rm -rf {bin_path} {hivisionai_path} {sdk_path}") | |
# 删除完毕,开始export | |
os.system(f'pip3 install {os.path.join(new_dir, "**.whl")} -t {export_path}') | |
hivisionai_utils.download_dependence(export_path) | |
# 将下载下来的文件夹删除 | |
os.system(f'rm -rf {target_zip} && rm -rf {new_dir}') | |
print("Done.") | |
def hy_func_init(force): | |
""" | |
在HY-func目录下使用hivisionai --init,可以自动将需要的依赖下载到指定位置 | |
不过对于比较大的模型——修复模型而言,需要手动下载 | |
Args: | |
force: 如果force为True,则会强制重新下载所有的内容,包括修复模型这种比较大的模型 | |
Returns: | |
程序执行完毕,会将一些必要的依赖也下载完毕 | |
""" | |
cwd = HivisionaiParams.getcwd | |
# 判断当前文件夹是否是HY-func | |
dirName = os.path.basename(cwd) | |
assert dirName == "HY-func", "请在正确的文件目录下初始化HY-func!" | |
# 需要下载的内容会存放在HivisionaiParams的functionDependence变量下 | |
functionDependence = HivisionaiParams.functionDependence | |
# 下载配置文件 | |
configs = functionDependence["configs"] | |
print("正在下载配置文件...") | |
for config in configs: | |
if not force and os.path.exists(config['save_path']): | |
print(f"[pass]: {os.path.basename(config['url'])}") | |
continue | |
print(f"[Download]: {config['url']}") | |
resp = requests.get(config['url']) | |
# json文件存储在text区域,但是其他的不一定 | |
open(os.path.join(cwd, config['save_path']), 'w').write(resp.text) | |
# 其他文件,提示访问notion文档 | |
print(f"[NOTICE]: 一切准备就绪,请访问下面的文档下载剩下的模型文件:\n{functionDependence['weights']}") | |
def hy_func_deploy(functionName: str = None, functionPath: str = None): | |
""" | |
在HY-func目录下使用此命令,并且随附功能函数的名称,就可以将HY-func的部署版放到桌面上 | |
但是需要注意的是,本方式不适合修复功能使用,修复功能依旧需要手动制作镜像 | |
Args: | |
functionName: 功能函数名称 | |
functionPath: 需要注册的HY-func路径 | |
Returns: | |
程序执行完毕,桌面会出现一个同名文件夹 | |
""" | |
# 为了代码撰写的方便,这里仅仅把模型文件删除,其余配置文件保留 | |
# 为了实现在任意位置输入hivisionai --deploy funcName都能成功,在使用前需要在.hivisionai/user_config.json中注册 | |
# print(functionName, functionPath) | |
if functionPath is not None: | |
# 更新/添加路径 | |
# functionPath为相对于使用路径的路径 | |
assert os.path.basename(functionPath) == "HY-func", "所指向路径非HY-func!" | |
func_path = os.path.join(HivisionaiParams.getcwd, functionPath) | |
assert os.path.join(func_path), f"路径不存在: {func_path}" | |
# functionPath的路径写到user_config当中 | |
user_config = json.load(open(HivisionaiParams.cloud_config_save, 'rb')) | |
user_config["func_path"] = func_path | |
open(HivisionaiParams.cloud_config_save, 'w').write(json.dumps(user_config)) | |
print("HY-func全局路径保存成功!") | |
try: | |
user_config = json.load(open(HivisionaiParams.cloud_config_save, 'rb')) | |
func_path = user_config['func_path'] | |
except KeyError: | |
return print("请先使用-p命令注册全局HY-func路径!") | |
# 此时func_path必然存在 | |
# print(os.listdir(func_path)) | |
assert functionName in os.listdir(func_path), functionName + "功能不存在!" | |
func_path_deploy = os.path.join(func_path, functionName) | |
# 开始复制文件到指定目录 | |
# 我们默认移动到Desktop目录下,如果没有此目录,需要先创建一个 | |
target_dir = os.path.join(HivisionaiParams.download_path, "Desktop") | |
assert os.path.exists(target_dir), target_dir + "文件路径不存在,你需要先创建一下!" | |
# 开始移动 | |
target_dir = os.path.join(target_dir, functionName) | |
print("正在复制需要部署的文件...") | |
os.system(f"rm -rf {target_dir}") | |
os.system(f'cp -rf {func_path_deploy} {target_dir}') | |
os.system(f"cp -rf {os.path.join(func_path, '_lib')} {target_dir}") | |
os.system(f"cp -rf {os.path.join(func_path, '_service')} {target_dir}") | |
# 生成最新的hivisionai | |
print("正在生成hivisionai代码包...") | |
os.system(f'hivisionai -t {target_dir}') | |
# 移动完毕,删除模型文件 | |
print("移动完毕,正在删除不需要的文件...") | |
# 模型文件 | |
os.system(f"rm -rf {os.path.join(target_dir, '_lib', 'weights', '**')}") | |
# hivisionai生成时的多余文件 | |
os.system(f"rm -rf {os.path.join(target_dir, 'bin')} {os.path.join(target_dir, 'HY_sdk**')}") | |
print("部署文件生成成功,你可以开始部署了!") | |
hivisionai_utils = HivisionaiUtils() | |
def entry_point(): | |
parser = ArgumentParser() | |
# 查看版本号 | |
parser.add_argument("-v", "--version", action="store_true", help="View the current HY-sdk version, which does not represent the final cloud version.") | |
# 自动更新 | |
parser.add_argument("-u", "--upgrade", nargs='?', const="-1", type=str, help="Automatically update HY-sdk to the latest version") | |
# 查找云端的HY-sdk版本 | |
parser.add_argument("-l", "--list", action="store_true", help="Find HY-sdk versions of the cloud, and keep up to ten") | |
# 下载云端的版本到本地路径 | |
parser.add_argument("-t", "--export", nargs='?', const="./", help="Add a path parameter to automatically download the latest version of sdk to this path. If there are no parameters, the default is the current path") | |
# 强制更新附带参数,当一个功能需要强制执行一遍的时候,需要附带此参数 | |
parser.add_argument("-f", "--force", action="store_true", help="Enforcement of other functions, execution of a single parameter is meaningless") | |
# 初始化HY-func | |
parser.add_argument("--init", action="store_true", help="Initialization HY-func") | |
# 部署HY-func | |
parser.add_argument("-d", "--deploy", nargs='?', const="-1", type=str, help="Deploy HY-func") | |
# 涉及注册一些自定义内容的时候,需要附带此参数,并写上自定义内容 | |
parser.add_argument("-p", "--param", nargs='?', const="-1", type=str, help="When registering some custom content, you need to attach this parameter and write the custom content.") | |
args = parser.parse_args() | |
if args.version: | |
print(version(HivisionaiParams.package_name)) | |
sys.exit() | |
if args.upgrade: | |
HivisionaiApps.upgrade(args.upgrade, args.force) | |
sys.exit() | |
if args.list: | |
HivisionaiApps.show_cloud_version() | |
sys.exit() | |
if args.export: | |
HivisionaiApps.export(args.export) | |
sys.exit() | |
if args.init: | |
HivisionaiApps.hy_func_init(args.force) | |
sys.exit() | |
if args.deploy: | |
HivisionaiApps.hy_func_deploy(args.deploy, args.param) | |
if __name__ == "__main__": | |
entry_point() | |