gordonchan's picture
Upload 51 files
cc30486 verified
# -*- 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):
"""
本类为一些基本工具类,包含代码复用相关内容
"""
@staticmethod
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!")
@staticmethod
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对外暴露的接口,为了代码规整性,这里使用类来对暴露接口进行调整
"""
@staticmethod
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)
@staticmethod
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}/**')
@staticmethod
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.")
@staticmethod
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']}")
@staticmethod
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()