diff --git a/export_build_info/__pycache__/export_already_build.cpython-310.pyc b/export_build_info/__pycache__/export_already_build.cpython-310.pyc new file mode 100644 index 0000000..fd97af8 Binary files /dev/null and b/export_build_info/__pycache__/export_already_build.cpython-310.pyc differ diff --git a/export_build_info/already.txt b/export_build_info/already.txt new file mode 100644 index 0000000..e69de29 diff --git a/export_build_info/check.py b/export_build_info/check.py new file mode 100644 index 0000000..df76b80 --- /dev/null +++ b/export_build_info/check.py @@ -0,0 +1,125 @@ +import os, sys, time, shlex, subprocess, shutil, requests, cv2, numpy as np +from PIL import Image +import platform,socket,atexit +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') + #sys.path.append('libs') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') +import config, libs, libs_db,main_service_db,common + +arrPids = [184219,184215,184209,184206,184201,184199,184164,184087,184076,184069,184059,184029,184013,183993,183952,183888,183882,183868,183824,183817,183815,183812,183791,183777,183715,183705,183670,183613,183573,182974,182983,182985,182989,182997,183007,183051,183060,183054,183053,183097,183119,183108,183139,183141,183155,183163,183169,183180,183191,183196,183214,183244,183266,183278,183275,183309,183402,183422,183440,182974,182964,182958,182956,182916,182855,182854,182820,182787,182800,182809,182780,182772,182770,182766,182754,182742,182740,182723,182707,182647,182643,182614,182595,182563,182557,182547,182546,182538,182530,182517,182498,182478,182452,182386,182352,182285,182266,182260,182230,182224] + +r12Pids = [184219,184215,184209,184069,184059,183868,183817,183791,183613,183573,182974,182983,182985,182989,182997,183007,183051,183060,183054,183053,183097,183119,183108,183139,183141,183155,183163,183169,183180,183191,183196,183214,183244,183266,183278,183275,183309,183402,183422,183440,182974,182964,182958,182855,182854] +print(f"处理的数据长度--{len(arrPids)}") +i=0 +noPids = '' +for pid in r12Pids: + pid = str(pid) + path = f"D://{pid}/mask" + #判断是否存在文件夹 + if not os.path.exists(path): + continue + + #判断文件夹是否为空 + if len(os.listdir(path)) == 0: + continue + + i+=1 + + + #检查 depth 和 mask 的文件创建时间是否差异比较大 + fileDepth = f"D://{pid}/depth" + fileMask = f"D://{pid}/mask" + + diffDepthTime = [] + dfffMaskTime = [] + + arrTimeDepth = [] + arrTimeMask = [] + for file in os.listdir(fileDepth): + createTime = os.path.getctime(f"{fileDepth}/{file}") + updateTime = os.path.getmtime(f"{fileDepth}/{file}") + # print(updateTime - createTime) + diffDepthTime.append(updateTime - createTime) + arrTimeDepth.append(os.path.getctime(f"{fileDepth}/{file}")) + + for file in os.listdir(fileMask): + createTime = os.path.getctime(f"{fileMask}/{file}") + updateTime = os.path.getmtime(f"{fileMask}/{file}") + dfffMaskTime.append(updateTime - createTime) + + arrTimeMask.append(os.path.getctime(f"{fileMask}/{file}")) + + #打印出 两者的 最大 和最小的时间信息 + print(f"pid: {pid} depth: { (max(arrTimeDepth) - min(arrTimeDepth))/60 }, diffdepth: {max(diffDepthTime)}, mask: { (max(arrTimeMask) - min(arrTimeMask))/60},diffmask: {max(dfffMaskTime)}") + # print(f"pid: {pid} diffdepth: {max(arrTimeDepth)} diffmask: {max(arrTimeMask)}") + + diffDepth = (max(arrTimeDepth) - min(arrTimeDepth))/60 + diffMask = (max(arrTimeMask) - min(arrTimeMask))/60 + + if diffDepth > 5 or diffMask > 5: + print(f"异常数据 pid: {pid} depth: {diffDepth} mask: {diffMask}") + break + + #复制文件到指定目录 + maskPath = f"D://{pid}/mask" + depthPath = f"D://{pid}/depth" + regPath = f"D://{pid}/reg" + imageList = f"D://{pid}/{pid}_imageList.txt" + outFila = f"D://{pid}/reg/{pid}_registration.out" + + sourcePath = f"D://model_info/{pid}" + if os.path.exists(sourcePath): + shutil.rmtree(sourcePath) + + if not os.path.exists(sourcePath): + os.makedirs(sourcePath) + + #复制文件 + shutil.copytree(maskPath, f"D://model_info/{pid}/mask") + shutil.copytree(depthPath, f"D://model_info/{pid}/depth") + shutil.copy(imageList, f"D://model_info/{pid}/{pid}_imageList.txt") + shutil.copy(outFila, f"D://model_info/{pid}/{pid}_registration.out") + + #遍历reg文件夹 + for file in os.listdir(regPath): + if file == f"{pid}_registration.out": + continue + + if "_1.jpg" in file: + sourceTempPath = f"D://model_info/{pid}/reg/photo1" + if not os.path.exists(sourceTempPath): + os.makedirs(sourceTempPath) + shutil.copy(f"{regPath}/{file}", f"{sourceTempPath}/{file}") + + if "_8.jpg" in file: + sourceTempPath = f"D://model_info/{pid}/reg/photo2" + if not os.path.exists(sourceTempPath): + os.makedirs(sourceTempPath) + shutil.copy(f"{regPath}/{file}", f"{sourceTempPath}/{file}") + + #shutil.copy(f"{regPath}/{file}", f"D://model_info/{pid}/reg/{file}") + + #移除 reg 里的 {pid}_registration.out + + + print(f"处理完成 pid: {pid}") + + + + + # shutil.copytree(f"D://{pid}", f"D://{pid}_bak") + + + + # cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + # -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}"'# -quit + # print(cmd) + # cmd = shlex.split(cmd) + # res = subprocess.run(cmd) + + + + +#print(i) \ No newline at end of file diff --git a/export_build_info/depth_mask_config.xml b/export_build_info/depth_mask_config.xml new file mode 100644 index 0000000..79e3937 --- /dev/null +++ b/export_build_info/depth_mask_config.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/export_build_info/err.pid b/export_build_info/err.pid new file mode 100644 index 0000000..e69de29 diff --git a/export_build_info/export_already_build.py b/export_build_info/export_already_build.py new file mode 100644 index 0000000..75cb8db --- /dev/null +++ b/export_build_info/export_already_build.py @@ -0,0 +1,206 @@ +import os, sys, shutil, subprocess, shlex,oss2,requests +sys.path.append('e:\\libs\\') +import config +import xml.etree.ElementTree as ET +import common +def is_already(pid): + + if not os.path.exists("D://make2/export_build_info/finished.txt"): + #创建文件 + with open("D://make2/export_build_info/finished.txt", 'w') as f: + f.write("") + return False + #读取已经完成的pid + with open("D://make2/export_build_info/finished.txt", 'r') as f: + lines = f.readlines() + for line in lines: + if str(pid) == str(line.strip()): + return True + return False + + + + + +def writeImageList(pid): + # pid = "185005" + imageListTxt = f"D://{pid}/{pid}_imageList.txt" + if not os.path.exists(imageListTxt): + with open(imageListTxt, 'w') as f: + f.write("") + filePath = f"D://{pid}//{pid}.rcproj" + tree = ET.parse(filePath) + root = tree.getroot() + fileName = [] + for input_tag in root.findall(".//input"): + tempFileName = input_tag.get("fileName") + if tempFileName: + fileName.append(tempFileName) + sourceFileNames = [] + for sourceFile in fileName: + tempSourceName = sourceFile.replace(f"D:\{pid}\photo1\\","") + tempSourceName = tempSourceName.replace(f"D:\{pid}\photo2\\","") + print("tempSourceName",tempSourceName) + #遍历指定的文件减价 + for file in os.listdir(f"D://{pid}/depthAndMask"): + if ".jpg.depth.exr" not in file: + continue + #截取图片名称 + tempName = file.replace(".depth.exr","") + if tempName == tempSourceName: + #sourceFileNames.append(sourceFile) + #写入到文件中 + print(f"写入到文件-{sourceFile}") + with open(imageListTxt, 'a+') as f: + f.write(f"{sourceFile}\n") + + +def exportInfo(pid): + newCopyFilePath = f"D://{pid}/{pid}.rcproj" + #判断是否存在目录,不存在则创建 + depthMaskPath = f"D://{pid}/depthAndMask" + #删除文件夹 + try: + shutil.rmtree(depthMaskPath) + except Exception as e: + print(e) + + + if not os.path.exists(f"D://{pid}/depthAndMask"): + os.makedirs(f"D://{pid}/depthAndMask") + + imageListTxt = f"D://{pid}/{pid}_imageList.txt" + try: + os.remove(imageListTxt) + except Exception as e: + print(e) + + if not os.path.exists(imageListTxt): + with open(imageListTxt, 'w') as f: + f.write("") + + maskPath = f"D://{pid}/mask" + + try: + shutil.rmtree(maskPath) + except Exception as e: + print(f"删除文件夹失败{e}") + if not os.path.exists(f"D://{pid}/mask"): + os.makedirs(f"D://{pid}/mask") + + depthPath = f"D://{pid}/depth" + try: + shutil.rmtree(depthPath) + except Exception as e: + print(e) + + if not os.path.exists(f"D://{pid}/depth"): + os.makedirs(f"D://{pid}/depth") + + registrationPath = f"D://{pid}/reg" + try: + shutil.rmtree(registrationPath) + except Exception as e: + print(e) + if not os.path.exists(registrationPath): + os.makedirs(registrationPath) + + #如果存在则执行打开命令 + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} -load "{newCopyFilePath}" -exportRegistration "D://{pid}/reg/{pid}_registration.out" "D://make2/config/registration_export_config.xml" -exportDepthAndMask "D://{pid}/depthAndMask" "D://make2/config/depth_mask_config.xml" -quit' + # print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + + writeImageList(pid) + + #删除掉 depthMaskPath 里 文件名 包含 _1. 的文件 + for file in os.listdir(depthMaskPath): + #将遍历出来的文件名进行判断,如果是_8.jpg.mask _1.jpg.mask 写入到pid_imgList.txt中 + # if "_8.jpg.mask" in file: + # jpgPath = f"D://{pid}/photo2/"+file.replace(".mask", "") + # jpgPath = jpgPath.replace(".png", "") + # print("_8",jpgPath) + # with open(imageListTxt, 'a+') as f: + # f.write(f"{jpgPath}\n") + + # if "_1.jpg.mask" in file: + # jpgPath = f"D://{pid}/photo1/"+file.replace(".mask", "") + # jpgPath = jpgPath.replace(".png", "") + # print("_1",jpgPath) + # with open(imageListTxt, 'a+') as f: + # f.write(f"{jpgPath}\n") + + if "_1." in file: + os.remove(f"{depthMaskPath}/{file}") + + if "_8.jpg.mask" in file: + try: + shutil.move(f"{depthMaskPath}/{file}",maskPath) + except Exception as e: + print(f"移动文件失败1{e}") + + + if "_8.jpg.depth" in file: + try: + shutil.move(f"{depthMaskPath}/{file}",depthPath) + except Exception as e: + print(f"移动文件失败2{e}") + + + writeImageList(str(pid)) + + #删除已存在数据,防止版本冲突 + try: + deleteList = [] + for obj in oss2.ObjectIterator(config.oss_bucket,prefix=f'objs/auto/{pid}/model_info_v2'): + deleteList.append(obj.key) + config.oss_bucket.batch_delete_objects(deleteList) + except Exception as e: + print("") + + #上传到oss 上 + #将 D:\{pid}\{pid}_registration.out 上传到 oss model_info + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/model_info_v2/{pid}_registration.out', f'D://{pid}/reg/{pid}_registration.out') + # imageList.txt + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/model_info_v2/{pid}_imageList.txt', imageListTxt) + if os.path.exists(f"{depthMaskPath}/imageList.txt"): + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/model_info_v2/imageList.txt', f"{depthMaskPath}/imageList.txt") + # depth 和 mask 上传 + print("上传 mask") + for file in os.listdir(maskPath): + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/model_info_v2/mask/{file}', f"{maskPath}/{file}") + + print("上传 depth") + for file in os.listdir(depthPath): + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/model_info_v2/depth/{file}', f"{depthPath}/{file}") + + #上传几遍照片 + print("上传 reg") + for file in os.listdir(registrationPath): + if "_1.jpg" in file: + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/model_info_v2/reg/photo1/{file}', f"{registrationPath}/{file}") + + if "_8.jpg" in file: + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/model_info_v2/reg/photo2/{file}', f"{registrationPath}/{file}") + + tempFile = f"D://{pid}//finish.txt" + #创建文件 + with open(tempFile, 'w') as f: + f.write("") + + print(f"完成的PID-{pid}") + + + #写入到指定的文件中 + with open("D://make2/pid.text", 'a+') as f: + f.write(f"{pid},") + + #删除文件 + # common.removeFolder(str(pid)) + # shutil.rmtree(f"D://{pid}",ignore_errors=True) + + #插入队列 + tempUrl = "https://mp.api.suwa3d.com/api/customerP3dLog/addGaussianRedisQueneu?pid="+str(pid) + requests.get(tempUrl) + +# exportInfo("181593") \ No newline at end of file diff --git a/export_build_info/finish.txt b/export_build_info/finish.txt new file mode 100644 index 0000000..4f0850d --- /dev/null +++ b/export_build_info/finish.txt @@ -0,0 +1,90 @@ +146134 +146134 +146598 +183716 +185381 +183400 +187752 +184871 +184956 +185326 +187651 +187802 +187811 +187800 +183096 +187804 +184956 +187808 +187898 +187865 +187870 +187886 +187912 +185102 +185102 +185102 +187450 +185102 +187989 +188170 +188117 +188128 +188200 +188297 +188310 +188344 +185101 +188425 +188428 +188431 +188560 +188494 +188564 +188757 +188779 +188807 +188865 +188827 +188910 +188907 +188913 +188942 +188964 +189045 +189051 +189203 +189214 +189218 +189228 +189246 +189242 +189373 +189312 +189388 +189913 +189911 +189932 +189946 +189106 +186427 +186544 +190092 +190112 +188681 +190138 +188688 +189172 +183292 +189178 +184093 +183080 +189608 +189625 +189618 +189170 +189623 +190151 +183309 +190214 +188521 diff --git a/export_build_info/finished.txt b/export_build_info/finished.txt new file mode 100644 index 0000000..e69de29 diff --git a/export_build_info/gen_xmps_for_build_info.py b/export_build_info/gen_xmps_for_build_info.py new file mode 100644 index 0000000..8a7a741 --- /dev/null +++ b/export_build_info/gen_xmps_for_build_info.py @@ -0,0 +1,124 @@ +import os, sys, time, shutil, subprocess, shlex, json,oss2 +import platform +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') + +import config, libs, libs_db + +def upload_xmp(pid): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 上传xmp文件之前先删除oss上的xmp文件所在目录...') + pid = str(pid) + psid = libs.getPSid(pid) + #移除掉旧的文件夹 + #config.oss_bucket.delete_object(f'xmps/{pid}/') + #删除oss 上的文件夹里的内容 + + #判断是否存在该目录 + if config.oss_bucket.object_exists(f'xmps/{psid}/') == True: + object_list = oss2.ObjectIterator(config.oss_bucket, prefix=f'xmps/{psid}/') + if not any(object_list): + config.oss_bucket.batch_delete_objects([obj.key for obj in object_list]) + + start_time = time.time() + workdir = os.path.join(config.workdir, pid) + ##psid = libs.getPSid(pid) + config.oss_bucket.put_object_from_file(f'xmps/{psid}/{psid}.rcbox', os.path.join(workdir, f'{pid}.rcbox')) + for xmp in os.listdir(os.path.join(workdir, 'photo1')): + if xmp.endswith('.xmp'): + config.oss_bucket.put_object_from_file(f'xmps/{psid}/mesh/{xmp}', os.path.join(workdir, 'photo1', xmp)) + for xmp in os.listdir(os.path.join(workdir, 'photo2')): + if xmp.endswith('.xmp'): + config.oss_bucket.put_object_from_file(f'xmps/{psid}/texture/{xmp}', os.path.join(workdir, 'photo2', xmp)) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} xmp文件上传完成,共费时{time.time() - start_time}秒') + +def main(pid, lock=False): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 开始计算相机位姿...') + start_time = time.time() + libs.down_from_oss(config.oss_bucket, config.workdir, pid) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 图片下载完成,共费时{libs.diff_time(start_time)}') + + start_time = time.time() + photo1_path = os.path.join(config.workdir, pid, 'photo1') + photo2_path = os.path.join(config.workdir, pid, 'photo2') + photos1_count = len(os.listdir(photo1_path)) + photos2_count = len(os.listdir(photo2_path)) + # if photos1_count + photos2_count < 164: + # print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} photo1数量{photos1_count} photo2数量{photos2_count},未能覆盖所有相机,是否继续计算相机位姿?') + # continue_or_not = input('是否继续计算相机位姿?(y/n)') + # if continue_or_not == 'y': + # pass + # else: + # sys.exit(0) + + if lock: + exportxmp = ' -exportXMP "D:\\make2\\config\\exportXMP.config.lock.xml" ' + else: + exportxmp = ' -exportXMP "D:\\make2\\config\\exportXMP.config.xml" ' + + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -addFolder "{os.path.join(config.workdir, pid, "photo1")}" -selectAllImages \ + -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ + -align -align \ + {exportxmp} \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + + # TODO:加入report相机位姿质量评估 + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} photo1相机位姿完成,共费时{libs.diff_time(start_time)}') + + for xmp in os.listdir(photo1_path): + if xmp.endswith('.xmp'): + shutil.copy(os.path.join(photo1_path, xmp), os.path.join(photo2_path, xmp.replace('_1.xmp', '_8.xmp'))) + + psid = libs.getPSid(pid) + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" {config.r["setTextureFalse"]} \ + -addFolder "{os.path.join(config.workdir, pid, "photo2")}" -selectAllImages \ + -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ + {libs.get_defineDistances(psid)} -update -align -align {config.r2["setRegion"]} \ + {exportxmp} \ + -exportReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} photo2相机位姿完成,共费时{libs.diff_time(start_time)}') + + # # TODO:加入report相机位姿质量评估 + # if not lock: + # upload_or_not = input('是否上传oss?(y/n)') + # if upload_or_not == 'y': + # upload_xmp(pid) + # #上传坐标的话要判断该影棚是否走新的建模系统,如果不是走新的建模系统,就要更新为走新的建模系统 + # libs_db.change_to_new_make_psid(psid) + # print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} xmp文件上传完成,共费时{libs.diff_time(start_time)}') + + # delete_or_not = input('是否删除本地文件?(y/n)') + # if delete_or_not == 'y': + # shutil.rmtree(os.path.join(config.workdir, pid)) + # print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 本地文件已删除') + # else: + # print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 本地文件未删除') + +if __name__ == '__main__': + if len(sys.argv) == 2: + pids = sys.argv[1].split(',') + for pid in pids: + main(pid) + elif len(sys.argv) == 3: + pids = sys.argv[1].split(',') + for pid in pids: + if sys.argv[2] == 'lock': + main(pid, lock=True) + else: + main(pid, lock=False) + else: + print(f'useage: python {sys.argv[0]} pid1,pid2,pid3 [lock]') + sys.exit(1) \ No newline at end of file diff --git a/export_build_info/main_step1.py b/export_build_info/main_step1.py new file mode 100644 index 0000000..ac4b257 --- /dev/null +++ b/export_build_info/main_step1.py @@ -0,0 +1,340 @@ +import os, sys, time, shlex, subprocess, shutil, requests, cv2, numpy as np +from PIL import Image +import platform,socket,atexit +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') + #sys.path.append('libs') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') +import config, libs, libs_db,main_service_db,common + +def filter_dark_texture_image(pid): + start_time = time.time() + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始检测射灯异常图片...') + def get_image_v(image): + # 图片左上角和右上角各取200*200区域,计算V通道均值 + left_rect = image[0:200, 0:200] + right_rect = image[0:200, -200:] + + left_hsv = cv2.cvtColor(left_rect, cv2.COLOR_BGR2HSV) + left_v = left_hsv[:, :, 2] + left_avg_v = np.mean(left_v) + # print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 图片左上角V通道均值:{left_avg_v}') + + right_hsv = cv2.cvtColor(right_rect, cv2.COLOR_BGR2HSV) + right_v = right_hsv[:, :, 2] + right_avg_v = np.mean(right_v) + # print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 图片右上角V通道均值:{right_avg_v}') + + return (left_avg_v + right_avg_v) / 2 + + v_list = [] + for filename in os.listdir(os.path.join(config.workdir, pid, 'photo2')): + if filename.endswith(".jpg"): + image = cv2.imread(os.path.join(config.workdir, pid, 'photo2', filename)) + v = get_image_v(image) + item = {'filename': filename, 'v': v} + v_list.append(item) + v_list.sort(key=lambda x: x['v']) + v_list = v_list[5: -5] + avg_v = np.mean([item['v'] for item in v_list]) + for item in v_list: + if abs(item['v'] - avg_v) > 50: + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 图片{item["filename"]} V通道值{item["v"]},低于平均值{avg_v},将不参与贴图') + libs.set_photo_join_type(config.workdir, pid, 'photo2', item['filename'].split('_')[0], mesh='1', texture='0') + + # 复制xmp文件到photo3目录,如果photo3目录存在的话 + if os.path.exists(os.path.join(config.workdir, pid, 'photo3')): + shutil.copyfile(os.path.join(config.workdir, pid, 'photo2', item['filename'].replace('jpg', 'xmp')), os.path.join(config.workdir, pid, 'photo3', item['filename'].replace('jpg', 'xmp'))) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 射灯异常图片检测完成,共费时{libs.diff_time(start_time)}') + +def detect_markers(psid, pid): + def fix_region(): + region_filename = os.path.join(config.workdir, pid, f'{pid}.rcbox') + with open(region_filename, 'r') as f: + lines = f.readlines() + lines = [line.replace('"NONE" globalCoordinateSystemWkt="NONE" globalCoordinateSystemName="NONE"', '"+proj=geocent +ellps=WGS84 +no_defs" globalCoordinateSystemName="local:1 - Euclidean"') for line in lines] + + start_time = time.time() + add_photo3 = ' ' + textpicCmd = ' ' + if os.path.exists(os.path.join(config.workdir, pid, 'photo3')): + add_photo3 = ' -addFolder "' + os.path.join(config.workdir, pid, 'photo3') + '" ' + + textpicCmd ='-selectImage "'+os.path.join(config.workdir,pid,'photo3')+'\*" -enableTexturingAndColoring true' + textpicCmd ='-selectImage "'+os.path.join(config.workdir,pid,'photo2')+'\*" -enableTexturingAndColoring false' + else: + textpicCmd ='-selectImage "'+os.path.join(config.workdir,pid,'photo2')+'\*" -enableTexturingAndColoring true' + + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -addFolder "{os.path.join(config.workdir, pid, "photo1")}" {config.r["setTextureFalse"]} -align -addFolder "{os.path.join(config.workdir, pid, "photo2")}" \ + {add_photo3} -align -selectAllImages \ + -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ + {libs.get_defineDistances(psid)} -align -align -update {config.r2["setRegion"]} \ + -exportXMP "D:\\make2\\config\\exportXMP.config.xml" \ + -exportControlPointsMeasurements "{os.path.join(config.workdir, pid, f"{pid}.controlPoints.csv")}" "D:\\make2\\config\\exportControlPoints.config.xml" \ + -exportReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \ + {textpicCmd} -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + time.sleep(3) + fix_region() + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 定位点检测完成, 共费时{libs.diff_time(start_time)}') + + if os.path.exists(os.path.join(config.workdir, pid, 'photo3')): + for filename in os.listdir(os.path.join(config.workdir, pid, 'photo2')): + if filename.endswith('_8.xmp'): + # photo3 exist, 设置photo2的xmp文件,不参与贴图 + libs.set_photo_join_type(config.workdir, pid, 'photo2', filename.split('_')[0], mesh='1', texture='0') + for filename in os.listdir(os.path.join(config.workdir, pid, 'photo3')): + if filename.endswith('_8.xmp'): + # photo3 exist, 设置photo3的xmp文件,不参与建模 + libs.set_photo_join_type(config.workdir, pid, 'photo3', filename.split('_')[0], mesh='0', texture='1') + +def step1(pid, experience=False, makeloop=True,task_distributed_id="",isNoColorTexture=""): + libs_db.start_task({"task_type": "make", "task_key": pid}) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 开始处理{pid}建模任务') + psid = libs.getPSid(pid) + + # 更新云端任务状态 + res = requests.post(config.urls['update_status_modeling_url'], data={'id': pid}) + print('更新建模中状态:', res.text) + + # 下载图片 + start_time = time.time() + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 开始下载图片...') + libs.down_from_oss(config.oss_bucket, config.workdir, pid) + try: + if str(psid) == "86": + #移除指定文件 + os.remove(f"D:\{pid}\photo1\\21_1.jpg") + os.remove(f"D:\{pid}\photo2\\21_8.jpg") + if str(psid) == "29": + #移除指定文件 + os.remove(f"D:\{pid}\photo1\\15_1.jpg") + os.remove(f"D:\{pid}\photo2\\15_8.jpg") + except Exception as e: + print("移除异常") + + #旋转图片 + os.system(f'python D:\\make2\\photo2rotate.py -w d:\ -p {pid} -i photo1 -o photo1') + os.system(f'python D:\\make2\\photo2rotate.py -w d:\ -p {pid} -i photo2 -o photo2') + + os.system(f'python D:\\make2\\export_build_info\\gen_xmps_for_build_info.py {pid}') + #print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 图片下载完成,共费时{libs.diff_time(start_time)}') + os.system(f'python main_step2.py {pid}') + # start_time = time.time() + # # TODO: 上报图片采集数量,更新影棚相机状态 + + # # 处理图片,如果experience=True,将图片缩减一半,已节省建模时间和算力成本 + # # if experience: + # # libs.resize_photos(os.path.join(config.workdir, pid, 'photo1')) + # # libs.resize_photos(os.path.join(config.workdir, pid, 'photo2')) + + # # 根据配置调整photo2曝光,均衡贴图亮度,可能产生photo3 + # libs.adjust_photos(config.workdir, pid) + + # # TODO: 检测模糊异常图片,上报微信通知 + # # TODO: 处理图片,去反光算法 + + # # 检测图片定位点,定位异常及时上报微信通知给客服人员,人工判断是否需要重新拍摄或更换地贴。定义定位点距离,导出相机位姿信息 + # detect_markers(psid, pid) + + # # 处理图片,检测photo2中的异常图片不参与贴图,以免破坏贴图效果,默认不检测射灯异常图片,以节省算力成本 + # if not makeloop: + # filter_dark_texture_image(pid) + + # # 处理图片,暗部提亮,提高贴图效果 + + # # TODO: 处理图片,去遮挡处理,提高建模与贴图质量 + + # # 加入photo2,计算重建区域,导出重建区域与相机位姿配置信息 + # # cal_reconstruction_region(psid, pid) + + # print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} step1图片预处理完成,共费时{libs.diff_time(start_time)}') + + # if os.path.exists(os.path.join(config.sharedir, pid)): + # shutil.rmtree(os.path.join(config.sharedir, pid), ignore_errors=True) + # shutil.copytree(os.path.join(config.workdir, pid), os.path.join(config.sharedir, pid)) + # print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} step1任务完成,移动到共享目录') + + + # #指定photo2某些图片不参与贴图 + # #是否不参与贴图,true 不参与贴图 + # if isNoColorTexture == "NoColorTexture": + # arrNoTextureColorPics = config.noTextureColorPics + # if arrNoTextureColorPics is not None and len(arrNoTextureColorPics) > 0: + # for camcerIndex in arrNoTextureColorPics: + # #拼装坐标文件名称 + # filename = os.path.join(config.sharedir, pid,"photo2",str(camcerIndex)+"_8.xmp") + # #判断文件是否存在 + # if os.path.exists(filename): + # #存在就设置不参与贴图 + # libs.set_photo_join_type(config.workdir, pid, 'photo2', camcerIndex, mesh='0', texture='0') + + + # #移除当前文件夹 + # shutil.rmtree(os.path.join(config.workdir, pid)) + # # TODO: 更新本地step1任务状态,加入step2任务队列 + # if task_distributed_id == "":#不是分布式任务的时候就自动往下个步骤走,是分布式任务的时候就就执行当前任务 + # if makeloop: + # os.system(f'python main_step2.py {pid}') + # else: + # os.system(f'python main_step2.py {pid}') + # # if os.path.exists(os.path.join(config.sharedir, pid)): + # # shutil.rmtree(os.path.join(config.sharedir, pid), ignore_errors=True) + # # shutil.move(os.path.join(config.workdir, pid), config.sharedir) + + # # print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} step1任务完成,移动到共享目录') + # else: + # #分布式服务执行完后,需要更新任务状态,更新字表的finished_at字段 + # main_service_db.update_task_distributed_detail({"task_distributed_id":task_distributed_id,"finished_at":time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}) + # print("step1完成,休息6s") + # time.sleep(4) + # return + + +def main(pid, experience=False, makeloop=True,task_distributed_id="",isNoColorTexture=""): + if pid == '0': + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 开始进入本地任务值守模式...') + while True: + # 取云端redis多个key任务,TODO:后续要改为api调用 + experience = False + pid = libs_db.get_task('make_experience') + if pid == '': + time.sleep(3) + pid = libs_db.get_task('make') + if pid == '': + time.sleep(3) + continue + else: + experience = False + step1(pid, experience, makeloop,task_distributed_id,isNoColorTexture) + else: + step1(pid, experience, makeloop,task_distributed_id,isNoColorTexture) + + + +def is_already(pid): + + + #读取已经完成的pid + with open("D://make2/export_build_info/finished.txt", 'r') as f: + lines = f.readlines() + for line in lines: + print("line",str(line.strip())) + if str(pid) == str(line.strip()): + print("匹配成功") + return True + return False + +if __name__ == '__main__': + isNoColorTexture = "" + if len(sys.argv) == 2: + v = sys.argv[1] + + #判断是否是数字 + if v.isdigit(): + #print(f"正在处理输入的pid-{v}") + main(str(v), experience=False, makeloop=False,task_distributed_id="",isNoColorTexture=isNoColorTexture) + else: + filePath = f"D://make2/export_build_info/pids/{v}.txt" + if not os.path.exists(filePath): + print(f"{filePath} 文件不存在") + + strPids = "" + with open("D://make2/pid.text","r") as f: + lines = f.readlines() + for line in lines: + strPids = str(line.strip()) + + errPids = "" + with open("D://make2/errPid.text","r") as f: + lines = f.readlines() + for line in lines: + errPids = str(line.strip()) + + arrErrPids = errPids.split(",") + + arrPids = strPids.split(",") + ii = 0 + with open(filePath, 'r') as f: + lines = f.readlines() + for line in lines: + pid = str(line.strip()) + if pid == "": + break + + #判断是否存在了目录或者已经是处理过了 + # tempFile = f"D://{pid}//finish.txt" + # if os.path.exists(tempFile): + # continue + + if str(pid) in arrErrPids: + continue + + if str(pid) in arrPids: + # print(f"已处理过{pid}") + ii += 1 + continue + + print(f"正在处理{pid}- 完成{ii}") + main(str(pid), experience=False, makeloop=False,task_distributed_id="",isNoColorTexture=isNoColorTexture) + ii += 1 + print(f"已经完成-{ii}") + + + else: + print("cmd like python main_step1.py pid/单人/多人/双人/动物/人物") + + + + + # # 取云端redis任务,完成第一步的数据预处理后,将数据放入共享存储目录,将第二步任务塞入本地mysql队列 + # # 默认循环值守,可传参数运行单一任务,以方便调试 + # #atexit.register(common.notify,socket.gethostname()+"建模任务已经停止") + # # pid = '0' + # isNoColorTexture = "" + # # if len(sys.argv) == 2: + # # pids = sys.argv[1].split(',') + # # for pid in pids: + # already = [184219,184215,184209,184069,184059,183868,183817,183791,183613,183054,182855,182854,184206,184201,184199,184164,184087,184076,184029,184013,183993,183952,183888,183882,183824,183815,183812] + + # arrPids = [184219,184215,184209,184206,184201,184199,184164,184087,184076,184069,184059,184029,184013,183993,183952,183888,183882,183868,183824,183817,183815,183812,183791,183777,183715,183705,183670,183613,183573,182974,182983,182985,182989,182997,183007,183051,183060,183054,183053,183097,183119,183108,183139,183141,183155,183163,183169,183180,183191,183196,183214,183244,183266,183278,183275,183309,183402,183422,183440,182974,182964,182958] + # #,182956,182916,182855,182854,182820,182787,182800,182809,182780,182772,182770,182766,182754,182742,182740,182723,182707,182647,182643,182614,182595,182563,182557,182547,182546,182538,182530,182517,182498,182478,182452,182386,182352,182285,182266,182260,182230,182224 + # i = 0 + # for pid in arrPids: + # if pid in already: + # continue + # print("正在处理-",pid) + + + + # if is_already(str(pid)) == True: + # continue + # # i += 1 + # # continue + # #写入到 + # with open("D://make2/export_build_info/finished.txt", 'a+') as f: + # f.write(f"{pid}\n") + + # main(str(pid), experience=False, makeloop=False,task_distributed_id="",isNoColorTexture=isNoColorTexture) + + # print("已处理-",i) + # #exit() + # # if len(sys.argv) == 3: + # # experience = False + # # if sys.argv[2] == '1': + # # print('演示测试...') + # # experience = False + # # elif sys.argv[2] == 'NoColorTexture': + # # isNoColorTexture = "NoColorTexture" + # # pids = sys.argv[1].split(',') + # # for pid in pids: + # # main(pid, experience=experience, makeloop=False,task_distributed_id="",isNoColorTexture=isNoColorTexture) + # # exit() + # # main(pid, experience=False, makeloop=True,task_distributed_id="",isNoColorTexture=isNoColorTexture) \ No newline at end of file diff --git a/export_build_info/main_step2.py b/export_build_info/main_step2.py new file mode 100644 index 0000000..148875f --- /dev/null +++ b/export_build_info/main_step2.py @@ -0,0 +1,267 @@ +import os, sys, time, shutil, subprocess, shlex +import platform +import pyautogui as ag +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') + #sys.path.append('libs') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') +import config, libs, libs_db,common,main_service_db,computerRecboxCenterByPoint +import export_already_build +redisLocal = config.redis_local +def load_model(pid): + cmd = f'{config.rcbin} {config.r1["init"]} -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}"' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + +def get_rcver(): + rcbin = '"C:\\Program Files\\Capturing Reality\\RealityCapture\\RealityCapture.exe"' + if os.path.getsize(rcbin[1:-1]) == 20783616: + return 1 + else: + return 2 + +def make3d(pid): + if get_rcver() == 1: # old version + #修改重建区域的大小 + common.change_rcbox_s(pid,"1") + #获取影棚id + # psid = libs.getPSid(pid) + # if int(psid) == 80: + # change_rcbox_deepth(str(pid),0.03) + psid = libs.getPSid(pid) + simplify_value = 1000000 * libs.getHeadCount(pid) + add_photo3 = ' ' + #判断是否存在photo3 + if os.path.exists(os.path.join(config.workdir, pid, 'photo3')): + add_photo3 = ' -addFolder "' + os.path.join(config.workdir, pid, 'photo3') + '" -align -align ' + #存在photo3 的情况下,photo2 不参与贴图 + #遍历获取photo2 目录下的所有文件 + directory = os.path.join(config.workdir, pid, 'photo2') + for root, dirs, files in os.walk(directory): + for file in files: + #判断file 是否以 _8.xmp 结尾 + if file.endswith("_8.xmp") == False: + continue + #修改文件内容不参与贴图 + libs.set_photo_join_type(config.workdir, pid, 'photo2', file.split("_")[0], mesh='0', texture='0') + else: + pass + #设置photo2参与贴图,因为有的时候贴图会黑 + directory = os.path.join(config.workdir, pid, 'photo2') + for root, dirs, files in os.walk(directory): + for file in files: + #判断file 是否以 _8.xmp 结尾 + if file.endswith("_8.xmp") == False: + continue + #修改文件内容不参与贴图 + libs.set_photo_join_type(config.workdir, pid, 'photo2', file.split("_")[0], mesh='0', texture='1') + + if get_rcver() == 1: # old version + + #判断oss 上是否存在 controlpoints 文件 + isExistPoint = common.isExistControlPointsOss(pid) + cmd = f'{config.rcbin} {config.r1["init"]} \ + -addFolder "{os.path.join(config.workdir, pid, "photo1")}" -addFolder "{os.path.join(config.workdir, pid, "photo2")}" {add_photo3} \ + -importControlPointsMeasurements "{os.path.join(config.workdir, pid, f"{pid}.controlPoints.csv")}" \ + -align -exportRegistration "{os.path.join(config.workdir,"make2", "config","exportRegistration.xml")}" "{os.path.join(config.workdir, pid,str(pid)+"_align.csv")}" -save' + + cmd = cmd + f' "{os.path.join(config.workdir, pid, f"{pid}_wait.rcproj")}"' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + time.sleep(2) + + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 定位点导入完成') + time.sleep(3) + # defind_distance + #死循环阻塞获取 + while True: + print("循环阻塞开始") + time.sleep(3) + #判断是否有 pid_1 的的值 + print(pid+"_1") + if redisLocal.lpos('model:auto_distance',pid+"_1") == None: + continue + shutil.move(os.path.join(config.workdir, pid, f'{pid}_wait.rcproj'), os.path.join(config.workdir, pid, f'{pid}.rcproj')) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 定义定位点距离完成') + #将 controlpoints_0.dat 文件拷贝到 oss 上作为公共的使用 + common.uploadControlPointsOss(pid) + + #最后处理掉redis中的值 + redisLocal.lrem('model:auto_distance', 0, pid+"_1") + break + print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),"循环阻塞结束,判断是否对齐成功") + + + #查看对齐的数量 + alignRes = common.isAlignNums(str(pid)) + if alignRes == "rebuild": + os.system(f'python d:\\make2\\tools\push_cmd.py rebuild {pid}') + os.system(f'python d:\\make2\\main_service.py') + return + #区域的设置 建模 + #update + cmdSmall = "-align" + #使用公共point文件的时候,就不能使用update , 而是要用 align + if isExistPoint == True: + cmdSmall = "-align" + #{config.r1["init"]} + + if common.task_need_high_model(pid): + if str(psid) == "41" or str(psid) == "85": + calulate_type = "-mvs" + else: + calulate_type = '-mvsHigh' + else: + calulate_type = '-mvs' + + #翻转相机的线向下 + cmd = f'{config.rcbin} -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" {cmdSmall} \ + -set "sfmEnableCameraPrior=True" -align -set "sfmEnableCameraPrior=False" -align -save "{os.path.join(config.workdir, pid, f"{pid}_point.rcproj")}" ' + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + #执行完成后命令会触发导出点云的定时程序 + while True: + print("判断是否导出点云文件循环阻塞开始") + time.sleep(3) + #判断是否有 pid_1 的的值 + print(pid+"_3") + if redisLocal.lpos('model:auto_distance',pid+"_3") == None: + continue + shutil.move(os.path.join(config.workdir, pid, f'{pid}_point.rcproj'), os.path.join(config.workdir, pid, f'{pid}.rcproj')) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 导出点云文件完成') + #最后处理掉redis中的值 + redisLocal.lrem('model:auto_distance', 0, pid+"_3") + break + + #处理点云文件 + centerRcboxValue = computerRecboxCenterByPoint.boxCenter(pid) + if centerRcboxValue > 0: + print("修改中心点的坐标",centerRcboxValue) + #修改中心点的值 + common.change_rcbox_center(pid,centerRcboxValue) + + cmd = f'{config.rcbin} -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -align \ + -set "sfmEnableCameraPrior=True" -align -set "sfmEnableCameraPrior=False" -align -setReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \ + {calulate_type} -modelSelectMaximalConnectedComponent -modelInvertSelection -modelRemoveSelectedTriangles -closeHoles -clean -simplify {simplify_value} -smooth -unwrap -calculateTexture -renameModel {pid} -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + #保存在导出 + # common.saveScreenImg(pid) + # time.sleep(1) + # ag.hotkey('alt', 'f4') + time.sleep(3) + #修改rcproj文件 + flag = common.changeRcprojFile(pid) + if flag == False: + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} rcproj文件不存在') + return + + #复制文件 + shutil.copy(os.path.join(config.workdir, pid, f'{pid}.rcproj'), os.path.join(config.workdir, pid, f'{pid}_screen.rcproj')) + time.sleep(2) + #打开工程文件 + cmd = f'{config.rcbin} -load "{os.path.join(config.workdir, pid, f"{pid}_screen.rcproj")}"' + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + + #创建指定文件夹 + if not os.path.exists(os.path.join(config.workdir, pid, "output")): + os.makedirs(os.path.join(config.workdir, pid, "output")) + + #判断output目录下是否有文件,有的话删除 + files = os.listdir(os.path.join(config.workdir, pid, "output")) + + #执行导出 + cmd = f'{config.rcbin} -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}"\ + -exportModel "{pid}" "{os.path.join(config.workdir, pid, "output", f"{pid}.obj")}" "d:\\make2\\config\\ModelExportParams102.xml" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + + + + else: # new version + #判断是否要进行高精模 + if common.task_need_high_model(pid): + if str(psid) == "41" or str(psid) == "85": + calulate_type = "calculateNormalModel" + else: + calulate_type = 'calculateHighModel' + else: + calulate_type = 'calculateNormalModel' + + #创建指定文件夹 + if not os.path.exists(os.path.join(config.workdir, pid, "output")): + os.makedirs(os.path.join(config.workdir, pid, "output")) + + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -{calulate_type} \ + -selectLargestModelComponent -invertTrianglesSelection -removeSelectedTriangles -simplify {simplify_value} -smooth -closeHoles -cleanModel -calculateTexture \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -exportSelectedModel "{os.path.join(config.workdir, pid, "output", f"{pid}.obj")}" "d:\\make2\\config\\ModelExportParams.xml" -quit'# -quit + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + + export_already_build.exportInfo(pid) + # #阻塞判断是否导出完成 + # while True: + # #判断 output 目录下是否存在 三个文件 + # files = os.listdir(os.path.join(config.workdir, pid, "output")) + # if len(files) >= 3: + # break + +def step2(pid,task_distributed_id=""): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始建模任务step2') + + #判断是否要从共享目录拷贝数据 + if os.path.exists(os.path.join(config.sharedir, pid)) and not os.path.exists(os.path.join(config.workdir, pid)): + shutil.move(os.path.join(config.sharedir, pid), config.workdir) + else: + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 目录{os.path.join(config.sharedir, pid)}不存在,或{os.path.join(config.workdir, pid)}已存在') + # return + #最后还是要判断有没有存在目录 + if not os.path.exists(os.path.join(config.workdir, pid)): + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 目录{os.path.join(config.workdir, pid)}不存在') + return + + start_time = time.time() + make3d(pid) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 建模任务step2完成,共费时{libs.diff_time(start_time)},任务已提交到step3') + # # 更新本地任务状态,加入step3任务队列 + # if task_distributed_id == "": + # os.system(f'python d:\\make2\\main_step3.py {pid}') + # else: + # #暂时 step2 step3 一起连续执行 + # print('step2 执行完,开始执行step3') + # os.system(f'python d:\\make2\\main_step3.py {pid}') + # main_service_db.update_task_distributed({"id":task_distributed_id,"status":2,"finished_at":time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}) + # main_service_db.update_task_distributed_detail({"task_distributed_id":task_distributed_id,"finished_at":time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}) + # #return + +def main(pid): + if pid == '0': + while True: + # 取本地mysql队列任务,完成第二步的建模任务 + + step2(pid) + else: + step2(pid) + +if __name__ == '__main__': + # 取本地mysql队列任务,完成第二步的建模任务 + # 默认循环值守,可传参数运行单一任务,以方便调试 + pid = '0' + if len(sys.argv) > 1: + pids = sys.argv[1].split(',') + for pid in pids: + main(pid) + exit() + main(pid) \ No newline at end of file diff --git a/export_build_info/manual_service.py b/export_build_info/manual_service.py new file mode 100644 index 0000000..7f4a8fd --- /dev/null +++ b/export_build_info/manual_service.py @@ -0,0 +1,185 @@ +import os, sys, time, shlex, subprocess, shutil, requests, cv2, numpy as np +from PIL import Image +import platform +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') + #sys.path.append('libs') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') +import config, libs, libs_db,main_service_db +# 2. 手动操作建模做成建模 +# 2.0 检测是否存在项目,不存在就下载,存在就清除项目其它无用的文件 +# 2.1 初始化工程, 根据参数 加入 photo1 或者 photo 2 的 照片, 对齐 , 导出坐标, 将坐标复制到 另外一个文件夹, 测距, 重建区域, 然后调用step2 + +#根据pid 检测是否存在项目,不存在就下载,存在就清除项目其它无用的文件 +def check_pid_file(pid): + #检测是否存在目录 + path = os.path.join(config.workdir, pid) + if not os.path.exists(path): + #不存在就在就下载目录 + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 开始计算相机位姿...') + start_time = time.time() + libs.down_from_oss(config.oss_bucket, config.workdir, pid) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 图片下载完成,共费时{libs.diff_time(start_time)}') + else: + #存在的话就检测是否有photo1 和 photo2 之外的目录或者文件,有的话就删除 + for file in os.listdir(path): + if file != 'photo1' and file != 'photo2': + if os.path.isfile(os.path.join(path, file)): + os.remove(os.path.join(path, file)) + else: + shutil.rmtree(os.path.join(path, file)) + + #判断photo1 和 photo2 目录里的文件是否存在xmp 文件,存在的话就删除 + for file in os.listdir(os.path.join(path, 'photo1')): + if file.endswith('.xmp'): + os.remove(os.path.join(path, 'photo1', file)) + + for file in os.listdir(os.path.join(path, 'photo2')): + if file.endswith('.xmp'): + os.remove(os.path.join(path, 'photo2', file)) + + +#根据参数初始化操作 +def cmd_run(pid,usePhoto = "1",lock=False): + pid = str(pid) + #检测文件并且下载处理文件 + check_pid_file(pid) + + start_time = time.time() + #文件路径 + photo1_path = os.path.join(config.workdir, pid, 'photo1') + photo2_path = os.path.join(config.workdir, pid, 'photo2') + #计算文件里的数量 + photos1_count = len(os.listdir(photo1_path)) + photos2_count = len(os.listdir(photo2_path)) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} photo1数量{photos1_count} photo2数量{photos2_count}') + + #xmp 坐标是要用 lock的 还是 unlock 的 + if lock: + exportxmp = ' -exportXMP "D:\\make2\\config\\exportXMP.config.lock.xml" ' + else: + exportxmp = ' -exportXMP "D:\\make2\\config\\exportXMP.config.xml" ' + + usePhoto = "photo"+str(usePhoto) + + #执行命令 + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName "{pid}" \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -addFolder "{os.path.join(config.workdir, pid, usePhoto)}" -selectAllImages \ + -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ + -align -align -align -align \ + {exportxmp} \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + + #根据参数转变路劲,复制photo1 里的xmp 文件到 photo2 里,或者 photo2 里的xmp 文件到 photo1 里 + sourceFile = photo1_path + targetFile = photo2_path + if usePhoto == "photo2": + sourceFile = photo2_path + targetFile = photo1_path + #复制xmp文件 + for xmp in os.listdir(sourceFile): + if xmp.endswith('.xmp'): + if usePhoto == "photo1": + shutil.copy(os.path.join(sourceFile, xmp), os.path.join(targetFile,xmp.replace('_1.xmp', '_8.xmp'))) + + if usePhoto == "photo2": + shutil.copy(os.path.join(sourceFile, xmp), os.path.join(targetFile,xmp.replace('_8.xmp', '_1.xmp'))) + + + + #如果是photo2的话,就要将photo2 的 xmp 重命名成 _8.xmp + # if usePhoto == "photo2": + # for xmp in os.listdir(sourceFile): + # if xmp.endswith('.xmp'): + # #重名名.xmp 结尾的文件 + # os.rename(os.path.join(sourceFile, xmp), os.path.join(sourceFile,xmp.replace('_1.xmp', '_8.xmp'))) + # print("坐标复制完成") + # exit() + + #将两组图片进行重新对齐 然后重建区域 + psid = libs.getPSid(pid) + cmd = f'{config.rcbin} -setInstanceName {pid} \ + -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" {config.r["setTextureFalse"]} \ + -addFolder "{targetFile}" -selectAllImages \ + -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ + {libs.get_defineDistances(psid)} -update -align -align -align -align {config.r2["setRegion"]} \ + {exportxmp} \ + -exportReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \ + -selectImage "{os.path.join(config.workdir,pid,"photo2")}/*" -enableTexturingAndColoring true \ + -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' + + + + + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} step1完成,共费时{libs.diff_time(start_time)}') + + #调用step2 + time.sleep(2) + # os.system(f'python main_step2.py {pid}') + os.system(f'python D://make2/export_build_info/main_step2.py {pid}') + + +def is_already(pid): + + + #读取已经完成的pid + with open("D://make2/export_build_info/finished.txt", 'r') as f: + lines = f.readlines() + for line in lines: + print("line",str(line.strip())) + if str(pid) == str(line.strip()): + print("匹配成功") + return True + return False + +if __name__ == '__main__': + already = [184219,184215,184209,184069,184059,183868,183817,183791,183613,183054,182855,182854,184206,184201,184199,184164,184087,184076,184029,184013,183993,183952,183888,183882,183824,183815,183812] + + arrPids = [184219,184215,184209,184206,184201,184199,184164,184087,184076,184069,184059,184029,184013,183993,183952,183888,183882,183868,183824,183817,183815,183812,183791,183777,183715,183705,183670,183613,183573,182974,182983,182985,182989,182997,183007,183051,183060,183054,183053,183097,183119,183108,183139,183141,183155,183163,183169,183180,183191,183196,183214,183244,183266,183278,183275,183309,183402,183422,183440,182974,182964,182958,182956,182916,182855,182854,182820,182787,182800,182809,182780,182772,182770,182766,182754,182742,182740,182723,182707,182647,182643,182614,182595,182563,182557,182547,182546,182538,182530,182517,182498,182478,182452,182386,182352,182285,182266,182260,182230,182224] + for pid in arrPids: + if pid in already: + continue + print("正在处理-",pid) + + + + if is_already(str(pid)) == True: + continue + # i += 1 + # continue + #写入到 + with open("D://make2/export_build_info/finished.txt", 'a+') as f: + f.write(f"{pid}\n") + + + cmd_run(str(pid),usePhoto = "1",lock=False) + + # if len(sys.argv) == 2: + # pids = sys.argv[1].split(',') + # for pid in pids: + # cmd_run(pid,usePhoto = "1",lock=False) + # elif len(sys.argv) == 3: + # pids = sys.argv[1].split(',') + # for pid in pids: + # if sys.argv[2] == '2': + # cmd_run(pid,usePhoto = "2",lock=False) + # else: + # cmd_run(pid,usePhoto = "1",lock=False) + # elif len(sys.argv) == 4: + # pids = sys.argv[1].split(',') + # usePhoto = sys.argv[2] + # lock = sys.argv[3] + # for pid in pids: + # cmd_run(pid,usePhoto = usePhoto,lock=lock) + + # else: + # print(f'useage: python {sys.argv[0]} pid1,pid2,pid3 photo = 1/2 lock = True/False') + # sys.exit(1) \ No newline at end of file diff --git a/export_build_info/pids/人物.txt b/export_build_info/pids/人物.txt new file mode 100644 index 0000000..b02645c --- /dev/null +++ b/export_build_info/pids/人物.txt @@ -0,0 +1,27 @@ +181561 +181559 +180990 +180772 +180538 +180256 +179927 +179657 +179627 +178838 +178714 +178616 +178604 +178332 +177920 +176655 +184102 +183728 +183538 +183524 +183274 +183316 +183345 +182867 +182658 +182638 +182560 diff --git a/export_build_info/pids/动物.txt b/export_build_info/pids/动物.txt new file mode 100644 index 0000000..58a4003 --- /dev/null +++ b/export_build_info/pids/动物.txt @@ -0,0 +1,28 @@ +181307 +181287 +181267 +180402 +179617 +179613 +178991 +178892 +178814 +178787 +178471 +178469 +178400 +178156 +177899 +177894 +176236 +184133 +184039 +183848 +183769 +183637 +183127 +183238 +182450 +182031 +181977 +181960 diff --git a/export_build_info/pids/单人.txt b/export_build_info/pids/单人.txt new file mode 100644 index 0000000..cef740f --- /dev/null +++ b/export_build_info/pids/单人.txt @@ -0,0 +1,393 @@ +181763 +181757 +181754 +181687 +181652 +181642 +181619 +181597 +181574 +181542 +181526 +181515 +181482 +181435 +181421 +181408 +181391 +181388 +181367 +181333 +181329 +181327 +181304 +181274 +181261 +181257 +181248 +181232 +181231 +181229 +181205 +181204 +181203 +181202 +181191 +181188 +181177 +181176 +181145 +181143 +181139 +181117 +181115 +181108 +181097 +181062 +181050 +181046 +181040 +181039 +181023 +181020 +180961 +180958 +180944 +180943 +180904 +180903 +180869 +180864 +180862 +180846 +180839 +180819 +180781 +180776 +180771 +180770 +180670 +180661 +180648 +180593 +180591 +180588 +180581 +180461 +180455 +180450 +180429 +180424 +180394 +180327 +180314 +180308 +180297 +180292 +180291 +180281 +180273 +180225 +180185 +180167 +180156 +180145 +180125 +180085 +180084 +180082 +179980 +179978 +179975 +179942 +179935 +179895 +179861 +179850 +179849 +179815 +179722 +179704 +179695 +179688 +179676 +179652 +179646 +179623 +179621 +179585 +179555 +179551 +179493 +179489 +179474 +179471 +179470 +179461 +179457 +179451 +179430 +179355 +179344 +179331 +179316 +179282 +179229 +179226 +179219 +179205 +179204 +179166 +179160 +179151 +179149 +179147 +179139 +178752 +178743 +178677 +178675 +178674 +178624 +178606 +178594 +178591 +178528 +178526 +178521 +178520 +178519 +178493 +178457 +178452 +178447 +178390 +178388 +178369 +178364 +178349 +178348 +178347 +178330 +178319 +178303 +178302 +178294 +178287 +178269 +178236 +178220 +178219 +178214 +178203 +178191 +178187 +178164 +178159 +178129 +178100 +178098 +178054 +178051 +178048 +178042 +178034 +178016 +177467 +177465 +177453 +177449 +177447 +177443 +177431 +177430 +177412 +177360 +177353 +177350 +177337 +177327 +177309 +178008 +177999 +177998 +177992 +177991 +177984 +177973 +177972 +177971 +177948 +177852 +177830 +177820 +177812 +177763 +177706 +177681 +177659 +177648 +177647 +177641 +177628 +177613 +177611 +177550 +177293 +177288 +177280 +177277 +177265 +177262 +177257 +177225 +177213 +177198 +177172 +177159 +177146 +177152 +177108 +177095 +177092 +177048 +177046 +177035 +176822 +176752 +176739 +176688 +176678 +176631 +176609 +176602 +176525 +176338 +176333 +176314 +176305 +176249 +176238 +184219 +184215 +184209 +184206 +184201 +184199 +184164 +184087 +184076 +184069 +184059 +184029 +184013 +183993 +183952 +183888 +183882 +183868 +183824 +183817 +183815 +183812 +183791 +183777 +183715 +183705 +183670 +183613 +183573 +182974 +182983 +182985 +182989 +182997 +183007 +183051 +183060 +183054 +183053 +183097 +183119 +183108 +183139 +183141 +183155 +183163 +183169 +183180 +183191 +183196 +183214 +183244 +183266 +183278 +183275 +183309 +183402 +183422 +183440 +182974 +182964 +182958 +182956 +182916 +182855 +182854 +182820 +182787 +182800 +182809 +182780 +182772 +182770 +182766 +182754 +182742 +182740 +182723 +182707 +182647 +182643 +182614 +182595 +182563 +182557 +182547 +182546 +182538 +182530 +182517 +182498 +182478 +182452 +182386 +182352 +182285 +182266 +182260 +182230 +182224 +182223 +182191 +182185 +182182 +182179 +182106 +182101 +182072 +181966 +181965 +181937 +181928 +181925 +181924 +181923 +181900 +181895 +181860 +181847 +181836 +181833 +181811 +181809 diff --git a/export_build_info/pids/双人.txt b/export_build_info/pids/双人.txt new file mode 100644 index 0000000..7be7926 --- /dev/null +++ b/export_build_info/pids/双人.txt @@ -0,0 +1,307 @@ +181593 +181384 +181337 +181318 +181263 +181240 +181211 +181197 +181187 +181140 +181103 +181091 +181054 +181027 +180763 +180690 +180684 +180677 +180599 +180576 +180389 +180342 +180337 +180277 +180265 +180253 +180101 +180083 +179905 +179842 +179811 +179808 +179791 +179786 +179700 +179634 +179593 +179576 +179485 +179459 +179400 +179351 +178995 +178898 +178750 +178746 +178723 +178680 +178673 +178654 +178527 +178482 +178465 +178430 +178413 +178409 +178297 +178286 +178281 +178105 +178062 +178026 +178025 +178019 +178017 +178004 +177851 +177678 +177677 +177646 +177545 +177520 +177300 +177080 +176757 +176743 +176648 +176290 +176280 +176273 +176247 +184082 +184074 +184068 +184061 +184041 +183893 +183890 +183810 +183755 +183680 +183656 +183592 +183591 +183589 +183025 +183086 +183104 +183162 +183444 +182907 +182915 +182871 +182804 +182778 +182718 +182675 +182664 +182625 +182623 +182542 +182510 +182473 +182455 +182388 +182365 +182343 +182316 +181957 +181948 +181941 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/export_build_info/pids/多人.txt b/export_build_info/pids/多人.txt new file mode 100644 index 0000000..a12d04f --- /dev/null +++ b/export_build_info/pids/多人.txt @@ -0,0 +1,28 @@ +180891 +180673 +180584 +180468 +180164 +180091 +179992 +179962 +178627 +178416 +178255 +178066 +177983 +177722 +183832 +183677 +183183 +183351 +183361 +183360 +182853 +182715 +182630 +182367 +182253 +182057 +181868 +181856 diff --git a/export_build_info/registration_export_config.xml b/export_build_info/registration_export_config.xml new file mode 100644 index 0000000..6bd1c35 --- /dev/null +++ b/export_build_info/registration_export_config.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/export_build_info/task_distributed_error.log b/export_build_info/task_distributed_error.log new file mode 100644 index 0000000..e69de29 diff --git a/install.txt b/install.txt new file mode 100644 index 0000000..0dc7a86 --- /dev/null +++ b/install.txt @@ -0,0 +1,13 @@ +pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple +pip config set install.trusted-host pypi.tuna.tsinghua.edu.cn + +python -m pip install --upgrade pip +pip install oss2 redis MySQLdb pillow numpy opencv-python bpy tqdm pyautogui psutil pywin32 pymysql + + +config +set bin="C:\Program Files\Capturing Reality\RealityCapture\RealityCapture.exe" +%bin% -disableOnlineCommunication -setInstanceName %pid% +%bin% -disableOnlineCommunication -delegateTo %pid% +%bin% -set "appCacheLocation=ProjectFolder" + diff --git a/libs/config.py b/libs/config.py index 74a209f..fde509a 100644 --- a/libs/config.py +++ b/libs/config.py @@ -49,6 +49,15 @@ mysql_dong = { "charset": "utf8mb4" } +mysql_task_rc = { + "host": '172.31.1.254',#"172.16.20.13", + "port": 23306, + "user": "pi", + "password": "ph2008", + "db": "suwa3d", + "charset": "utf8mb4" +} + if platform.system() == 'Windows': workdir = 'D:\\' sharedir = 'E:\\' @@ -115,7 +124,7 @@ r2 = { #new_make_psids = ['1', '17', '29', '44', '54', '55', '63', '65', '77', '79', '80', '85', '86'] #企业微信通知人员 -notify_user_Ids = ["DongZhangXi","YouShui"] +notify_user_Ids = ["DongZhangXi"] #任务运行超时时间设定 ,单位秒 task_run_timeout = { diff --git a/libs/libs.py b/libs/libs.py index 9489503..d8950b1 100644 --- a/libs/libs.py +++ b/libs/libs.py @@ -330,4 +330,52 @@ def down_obj_from_oss_for_fix(workdir, pid, action): # print('正在下载:', file.key) localfile = os.path.join(workdir, action, pid, filename) config.oss_bucket.get_object_to_file(file.key, localfile) - return obj_filename \ No newline at end of file + return obj_filename + +def down_obj_from_oss_for_repeat_texture(workdir, pid, action): + print(os.path.join(workdir, pid)) + if not os.path.exists(os.path.join(workdir, pid)): + print("目录不存在,无法处理") + return "error" + + #判断是否有专门新建的目录用来重新贴图 + newPath = os.path.join(workdir, pid,'repeat_texture','completion_cloud') + if not os.path.exists(newPath): + os.makedirs(newPath) + + + # 根据前缀获取文件列表 + prefix = f'objs/{action}/{pid}/ai/repeat_texture/completion_cloud' + filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix) + print('正在下载:', prefix) + for file in filelist: + filename = file.key.split('/')[-1] + if filename == "": + continue + localfile = os.path.join(newPath, filename) + config.oss_bucket.get_object_to_file(file.key, localfile) + return newPath + + + + +def down_obj_from_oss_auto(pid,action,pathStr): + #pathStr = os.path.join(workdir, str(pid)+"_ai") + if not os.path.exists(pathStr): + os.makedirs(pathStr) + + # 根据前缀获取文件列表 + prefix = f'objs/{action}/{pid}' + filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix) + print('正在下载:', prefix) + for file in filelist: + filename = file.key.split('/')[-1] + if filename == "": + continue + + if filename != f"{pid}.jpg" and filename != f"{pid}.obj" and filename != f"{pid}.mtl": + continue + + print(filename) + localfile = os.path.join(pathStr, filename) + config.oss_bucket.get_object_to_file(file.key, localfile) \ No newline at end of file diff --git a/libs/libs_db_repeat_texture.py b/libs/libs_db_repeat_texture.py new file mode 100644 index 0000000..00d522e --- /dev/null +++ b/libs/libs_db_repeat_texture.py @@ -0,0 +1,83 @@ +# mysql数据库常用任务函数封装 +import pymysql, socket, time +import config + +#公共连接库 +def pymysqlAlias(): + return pymysql.connect( + host=config.mysql_gpu['host'], + port=config.mysql_gpu['port'], + user=config.mysql_gpu['user'], + password=config.mysql_gpu['password'], + db=config.mysql_gpu['db'], + charset=config.mysql_gpu['charset'],) + + + +# 新增新的任务 +def add_reapeat_texture_task(data): + try: + + resExist = is_exist(data["pid"]) + if resExist == True: + return + + with pymysqlAlias() as conn: + cursor = conn.cursor() + sql = f'insert into task_repeat_texture (pid,heads,createTime) values ("{data["pid"]}","{data["heads"]}","{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}")' + print(f'sql: {sql}') + cursor.execute(sql) + conn.commit() + except Exception as e: + print(f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())} 执行add_task({data})异常: {str(e)}") + + +# +def is_exist(pid): + try: + with pymysqlAlias() as conn: + cursor = conn.cursor() + sql = f'select count(*) from task_repeat_texture where pid = {pid} and status = 0' + print(f'sql: {sql}') + cursor.execute(sql) + result = cursor.fetchone() + if result[0] > 0: + return True + else: + return False + except Exception as e: + print(f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())} 执行is_new_make_psid()异常: {str(e)}") + return "error" + + +def get_pid_task(): + try: + with pymysqlAlias() as conn: + cursor = conn.cursor() + sql = f'select * from task_repeat_texture where status = 1 limit 1' + print(f'sql: {sql}') + cursor.execute(sql) + result = cursor.fetchone() + if result: + return result + else: + return None + except Exception as e: + print(f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())} 执行get_pid_task()异常: {str(e)}") + return "error" + + + +#更新状态 +def update_status(id,status): + try: + with pymysqlAlias() as conn: + cursor = conn.cursor() + + hostname = socket.gethostname() + sql = f'update task_repeat_texture set status = {status},updateTime = now() where id = {id}' + print(f'开始任务sql: {sql}') + cursor.execute(sql) + conn.commit() + except Exception as e: + print(f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())} 执行update_status()异常: {str(e)}") \ No newline at end of file diff --git a/libs/libs_db_task_rc.py b/libs/libs_db_task_rc.py new file mode 100644 index 0000000..087e5cc --- /dev/null +++ b/libs/libs_db_task_rc.py @@ -0,0 +1,30 @@ +# mysql数据库常用任务函数封装 +import pymysql, socket, time +import config + +#公共连接库 +def pymysqlAlias(): + return pymysql.connect( + host=config.mysql_task_rc['host'], + port=config.mysql_task_rc['port'], + user=config.mysql_task_rc['user'], + password=config.mysql_task_rc['password'], + db=config.mysql_task_rc['db'], + charset=config.mysql_task_rc['charset'],) + +# 获取新的任务 +def get_task_rc_count(): + try: + with pymysqlAlias() as conn: + cursor = conn.cursor() + sql = f'select count(*) from task_distributed where status != 2 and created_at >= "2024-09-02 00:00:00"' + # print(f'sql: {sql}') + cursor.execute(sql) + data = cursor.fetchone() + if data: + return data[0] + else: + return '' + except Exception as e: + print(f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())} get_task_rc_count()异常: {str(e)}") + return '' \ No newline at end of file diff --git a/main_repeat_texture.py b/main_repeat_texture.py new file mode 100644 index 0000000..cbfd5ad --- /dev/null +++ b/main_repeat_texture.py @@ -0,0 +1,101 @@ +#读取需要重新贴图的数据 +import os,sys,time,shlex,subprocess,shutil +import platform +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') + #sys.path.append('libs') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') +import libs_db_repeat_texture +import libs,config + + +#读取 数据库的信息 +def get_pid(): + data = libs_db_repeat_texture.get_pid_task() + if data != None and data != "error": + return data[0],data[1] + else: + return None,None + +#开始处理任务 +def start_task(pid): + #下载Obj数据 + print(f"处理{pid}任务") + print("开始下载{pid}数据") + # pid = str("175635") + complateCloudPath = libs.down_obj_from_oss_for_repeat_texture(config.workdir, pid,"auto") + if complateCloudPath == "error": + return -1,"error-从oss上下载出问题" + #下载下来之后重新加载obj 和texture + print("complateCloudPath",complateCloudPath) + + + outPath = os.path.join(config.workdir, pid, "output") + if os.path.exists(outPath): + print("移除文件夹") + #移除文件夹 + shutil.rmtree(outPath) + + cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \ + -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ + -importModel "{os.path.join(complateCloudPath, f"{pid}.obj")}" \ + -calculateTexture -exportSelectedModel "{os.path.join(config.workdir, pid, "output", f"{pid}.obj")}" "d:\\make2\\config\\ModelExportParams0722.xml" -quit' + print(cmd) + cmd = shlex.split(cmd) + res = subprocess.run(cmd) + + #检查out目录文件夹是否有多个 贴图文件。如果是的话只保留最新的一个,并且重命名为初始图像的名称 + outFilePath = os.path.join(config.workdir, pid, "output") + arrTempImage = [] + for file in os.listdir(outFilePath): + if file.endswith(".jpg") or file.endswith(".png"): + #字符串替换 + fileTempName = file.replace(f"{pid}_u0_v0_diffuse","") + fileTempName = fileTempName.replace(".jpg","") + fileTempName = fileTempName.replace(".png","") + if "_" in fileTempName: + fileTempName = fileTempName.replace("_","") + arrTempImage.append(fileTempName) + print(arrTempImage) + if len(arrTempImage) > 1: + #获取 arrTempImage 中最大的值 + arrTempImage.sort() + print(arrTempImage) + #获取最大值 + maxValue = arrTempImage[-1] + + for i in arrTempImage: + if i == "": + filePath1 = os.path.join(config.workdir, pid, "output",pid+"_u0_v0_diffuse.jpg") + filePath2 = os.path.join(config.workdir, pid, "output",pid+"_u0_v0_diffuse.png") + else: + filePath1 = os.path.join(config.workdir, pid, "output",pid+"_u0_v0_diffuse_"+str(i)+".jpg") + filePath2 = os.path.join(config.workdir, pid, "output",pid+"_u0_v0_diffuse_"+str(i)+".png") + + if i != maxValue: + if os.path.exists(filePath1): + os.remove(filePath1) + if os.path.exists(filePath2): + os.remove(filePath2) + else: + if os.path.exists(filePath1): + os.rename(filePath1,os.path.join(config.workdir, pid, "output",pid+"_u0_v0_diffuse.jpg")) + if os.path.exists(filePath2): + os.rename(filePath2,os.path.join(config.workdir, pid, "output",pid+"_u0_v0_diffuse.jpg")) + #执行step3 + os.system(f'python d:\\make2\\main_step3.py {pid}') + return 3,"success" + +if __name__ == '__main__': + while True: + id,pid = get_pid() + print(f"读取的数据{pid}") + if pid != None: + #更新状态为2 + libs_db_repeat_texture.update_status(id,2) + statusres,res = start_task(str(pid)) + libs_db_repeat_texture.update_status(id,statusres) + else: + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}-没有任务') + time.sleep(10) diff --git a/main_step1.py b/main_step1.py index 62f9240..842053a 100644 --- a/main_step1.py +++ b/main_step1.py @@ -110,16 +110,14 @@ def step1(pid, experience=False, makeloop=True,task_distributed_id="",isNoColorT libs.down_from_oss(config.oss_bucket, config.workdir, pid) try: if str(psid) == "86": - #移除指定文件 os.remove(f"D:\{pid}\photo1\\21_1.jpg") os.remove(f"D:\{pid}\photo2\\21_8.jpg") + if str(psid) == "29": - #移除指定文件 os.remove(f"D:\{pid}\photo1\\15_1.jpg") os.remove(f"D:\{pid}\photo2\\15_8.jpg") except Exception as e: - print("移除异常") - + print("移除照片异常") os.system(f'python tools/downxmps.py {pid}') print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 图片下载完成,共费时{libs.diff_time(start_time)}') diff --git a/main_step2.py b/main_step2.py index 9da8c28..6e2746c 100644 --- a/main_step2.py +++ b/main_step2.py @@ -1,12 +1,13 @@ import os, sys, time, shutil, subprocess, shlex import platform import pyautogui as ag +from export_build_info import export_already_build if platform.system() == 'Windows': sys.path.append('e:\\libs\\') #sys.path.append('libs') else: sys.path.append('/data/deploy/make3d/make2/libs/') -import config, libs, libs_db,common,main_service_db,computerRecboxCenterByPoint +import config, libs, libs_db,common,main_service_db,computerRecboxCenterByPoint,libs_db_repeat_texture redisLocal = config.redis_local def load_model(pid): cmd = f'{config.rcbin} {config.r1["init"]} -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}"' @@ -216,6 +217,16 @@ def make3d(pid): if len(files) >= 3: break + # #插入需要重新贴图的任务表 + # libs_db_repeat_texture.add_reapeat_texture_task({"pid":pid,"heads":libs.getHeadCount(pid)}) + # #然后将导出的obj文件上传到oss上 + # config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/ai/repeat_texture/{pid}.obj', os.path.join(config.workdir, pid, 'output', f'{pid}.obj')) + # config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/ai/repeat_texture/{pid}.mtl', os.path.join(config.workdir, pid, 'output', f'{pid}.mtl')) + # config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/ai/repeat_texture/{pid}_u0_v0_diffuse.jpg', os.path.join(config.workdir, pid, 'output', f'{pid}_u0_v0_diffuse.jpg')) + # config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/ai/repeat_texture/{pid}.obj.rcInfo', os.path.join(config.workdir, pid, 'output', f'{pid}.obj.rcInfo')) + # #导出建模信息 + export_already_build.exportInfo(str(pid)) + def step2(pid,task_distributed_id=""): print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始建模任务step2') diff --git a/main_step3.py b/main_step3.py index 38c4dd5..87b4846 100644 --- a/main_step3.py +++ b/main_step3.py @@ -206,13 +206,11 @@ def step3(pid,task_distributed_id=""): export_and_update_obj(pid) resize_texture_and_reload_obj(pid) export_and_update_glbs(pid) + #向gpu服务器数据库添加一条数据用于AI自动修模,目前显示修复脸,不能上传更新建模状态,防止任务进入到修模部,人数为1才进行处理 + headCount = libs.getHeadCount(pid) + #if headCount == 1: + libs_db_gpu.add_task(data={"pid":pid,"cut_body":"face","heads":headCount}) - #向gpu数据库task 插入任务,用于开始执行AI修模 - headcount = libs.getHeadCount(pid) - #if headcount == 1: - libs_db_gpu.add_task(data={"pid":pid,"cut_body":"face","heads":headcount}) - - # 更新本地任务状态,更新云端任务状态 print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 模型后道处理完成,共费时{libs.diff_time(start_time)}') res = requests.post(config.urls['update_status_modelsuccess_url'], data={'id': pid}) @@ -228,6 +226,12 @@ def step3(pid,task_distributed_id=""): print("step3 已执行完成") #return common.removeFolder(str(pid)) + + os.system(f"python D:/make2/tools/get_weight_by_pid.py {pid}") + #执行模型优化,异步调用,再ai_repair 服务区上执行 + os.system(f"python D://make2/tools/optimize_model/main.py {pid}") + + def main(pid): if pid == '0': while True: diff --git a/manual_service.py b/manual_service.py index f3dae79..8aef172 100644 --- a/manual_service.py +++ b/manual_service.py @@ -68,7 +68,7 @@ def cmd_run(pid,usePhoto = "1",lock=False): -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \ -addFolder "{os.path.join(config.workdir, pid, usePhoto)}" -selectAllImages \ -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ - -align -align -align -align \ + -align -align \ {exportxmp} \ -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit' print(cmd) @@ -90,8 +90,6 @@ def cmd_run(pid,usePhoto = "1",lock=False): if usePhoto == "photo2": shutil.copy(os.path.join(sourceFile, xmp), os.path.join(targetFile,xmp.replace('_8.xmp', '_1.xmp'))) - - #如果是photo2的话,就要将photo2 的 xmp 重命名成 _8.xmp # if usePhoto == "photo2": # for xmp in os.listdir(sourceFile): @@ -107,7 +105,7 @@ def cmd_run(pid,usePhoto = "1",lock=False): -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" {config.r["setTextureFalse"]} \ -addFolder "{targetFile}" -selectAllImages \ -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ - {libs.get_defineDistances(psid)} -update -align -align -align -align {config.r2["setRegion"]} \ + {libs.get_defineDistances(psid)} -update -align -align {config.r2["setRegion"]} \ {exportxmp} \ -exportReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \ -selectImage "{os.path.join(config.workdir,pid,"photo2")}/*" -enableTexturingAndColoring true \ diff --git a/manual_single.py b/manual_single.py index 7d15497..c84220b 100644 --- a/manual_single.py +++ b/manual_single.py @@ -61,7 +61,7 @@ def cmd_run(pid,usePhoto = "2",lock=False): else: exportxmp = ' -exportXMP "D:\\make2\\config\\exportXMP.config.xml" ' - usePhoto = "photo"+str(usePhoto) + usePhoto = "photo2" #执行命令 cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName "{pid}" \ @@ -104,7 +104,7 @@ def cmd_run(pid,usePhoto = "2",lock=False): cmd = f'{config.rcbin} -setInstanceName {pid} \ -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}"\ -detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" \ - {libs.get_defineDistances(psid)} -update -align -align {config.r2["setRegion"]} \ + {libs.get_defineDistances(psid)} -update -align -align -align -align -align {config.r2["setRegion"]} \ {exportxmp} \ -exportReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \ -selectImage "{os.path.join(config.workdir,pid,"photo2")}/*" -enableTexturingAndColoring true \ diff --git a/pid.text b/pid.text new file mode 100644 index 0000000..bf2b155 --- /dev/null +++ b/pid.text @@ -0,0 +1,499 @@ +189002 +189819 +190152 +190141 +190204 +190210 +190221 +190234 +190257 +190264 +190297 +190377 +190238 +190409 +190417 +190144 +190233 +189879 +190227 +190463 +190510 +190531 +164671 +190241 +190547 +188521 +190609 +184467 +188521 +189772 +188521 +190724 +190506 +190509 +190511 +190578 +183895 +190584 +190605 +190611 +190620 +190635 +190683 +190684 +190694 +190697 +190708 +190726 +183494 +185507 +188521 +183494 +190751 +183895 +190754 +183895 +189002 +190784 +190783 +190795 +190812 +183895 +190823 +190837 +190910 +190838 +190866 +190905 +190915 +190927 +190944 +190939 +190932 +190379 +190948 +190959 +190980 +190975 +190927 +190996 +190981 +191001 +190927 +190992 +182361 +185080 +191045 +185416 +191069 +191082 +191081 +191103 +191118 +191116 +191152 +191180 +191125 +191227 +191238 +191246 +191234 +191275 +191320 +191283 +191354 +191388 +191435 +191441 +191431 +191380 +191340 +191350 +191297 +191472 +191255 +191268 +191501 +191192 +191156 +191517 +191515 +191525 +191532 +191261 +191422 +191024 +191542 +191587 +191597 +191676 +191678 +191711 +191736 +186628 +191874 +191937 +191924 +191945 +191887 +191868 +191981 +191885 +191838 +191882 +191965 +191822 +191870 +191920 +191894 +191811 +191739 +191681 +191790 +191738 +191699 +191781 +191733 +191746 +191793 +191829 +191834 +191907 +191927 +191969 +191990 +191998 +192005 +192021 +192023 +192026 +191771 +190829 +192038 +191906 +192095 +192107 +192098 +192123 +191066 +192150 +192161 +192178 +191210 +191849 +191832 +192093 +192199 +192208 +192202 +192213 +192211 +192204 +183783 +192220 +192267 +192223 +190880 +192288 +188877 +192283 +192291 +192079 +192077 +192072 +192063 +188201 +192343 +192353 +192367 +192373 +192222 +192399 +192392 +189979 +189988 +189996 +190692 +190709 +191955 +191912 +192444 +192438 +187727 +192479 +192473 +192469 +192476 +192140 +192143 +192158 +192498 +192169 +192175 +192186 +192187 +192197 +192214 +190362 +192236 +192239 +192241 +192242 +192244 +192248 +192304 +192261 +192421 +192425 +192514 +192162 +192548 +192550 +184779 +192495 +192490 +192583 +192586 +192591 +192593 +192595 +184756 +192235 +187195 +192378 +192622 +192129 +192570 +192566 +192480 +192481 +192484 +192482 +192532 +192624 +192630 +192682 +192674 +192690 +192701 +188185 +192725 +192729 +192608 +192732 +192717 +192609 +192611 +192612 +192615 +192744 +192643 +192650 +192653 +192659 +192776 +192664 +192676 +192680 +192758 +192759 +192767 +192775 +192782 +192783 +185658 +192838 +192873 +192875 +192905 +192895 +192869 +187718 +188167 +192929 +192940 +192955 +192947 +192907 +192908 +192887 +192979 +193038 +193003 +193005 +192875 +193112 +193101 +193085 +193082 +192986 +192944 +193132 +193141 +193058 +193160 +193074 +193217 +193239 +192208 +193285 +193260 +193287 +193288 +193298 +193281 +193335 +193332 +193349 +193165 +193185 +193010 +193231 +193364 +193295 +193313 +193339 +193354 +193357 +193360 +193375 +193376 +193377 +193378 +193353 +193193 +193454 +192317 +193405 +193468 +193475 +193483 +193479 +193486 +193508 +193504 +193511 +193503 +193520 +193562 +193571 +193560 +193237 +193569 +193591 +193590 +193593 +193617 +193615 +193637 +193625 +193642 +193646 +193683 +193671 +193677 +193616 +193702 +193700 +193604 +193721 +193745 +193770 +193768 +193758 +193762 +193636 +193652 +190141 +193771 +193781 +193813 +193806 +193812 +193818 +193835 +193863 +193907 +193832 +193896 +193903 +193906 +193380 +193383 +193384 +193925 +193386 +193480 +193387 +193666 +193711 +193757 +193934 +193778 +193795 +193809 +193822 +193817 +193843 +193918 +193876 +193922 +193567 +193564 +193551 +192320 +193117 +193961 +193969 +193524 +193547 +193990 +193995 +194012 +194022 +194027 +194021 +194069 +194048 +194035 +194070 +194015 +194018 +194093 +194098 +194144 +194104 +194112 +194119 +194108 +194126 +194153 +194163 +194044 +194047 +194206 +194004 +194229 +194218 +194240 +194242 +194278 +194271 +194293 +194081 +194173 +194176 +194211 +194245 +194270 +194285 +194292 +194294 +194296 +194300 +194347 +190480 +190483 +194258 +194266,194276,194444,194419,194454,194471,194470,194473,194475,194480,194493,194497,194499,194480,194460,192716,194465,194514,194515,194527,194501,194500,194526,194531,194536,194540,194551,194564,194577,194582,194580,187324,194599,194601,194628,194625,194623,189048,186982,194627,194508,194510,189048,194640,194642,194654,194685,194686,194707,194720,194719,194744,194734,194634,194738,194760,194765,194764,194768,194774,190697,194566,194356,194365,194369,194806,194376,194064,194772,194097,194773,194100,194103,194812,194810,194223,194181,194178,194589,194588,186685,194831,194830,194832,194834,194854,194817,194878,191283,191321,194900,194904,194858,194901,194885,194916,194924,194925,194802,194927,194932,194906,194908,194911,194912,194914,194915,194917,194920,194953,194926,194928,194930,194935,194957,194817,190531,189819,194989,194987,194995,194996,192305,194998,195015,195013,195023,195018,195021,195032,195005,195050,189864,195067,195072,195091,195026,195031,195104,195029,195102,195051,195063,195093,195125,195146,193800,195160,195172,195154,195184,194210,186610,191005,187854,193794, \ No newline at end of file diff --git a/step.text b/step.text new file mode 100644 index 0000000..f68ca32 --- /dev/null +++ b/step.text @@ -0,0 +1,5 @@ +old version + +init: + + '"C:\\Program Files\\Capturing Reality\\RealityCapture\\RealityCapture.exe" -disableOnlineCommunication -set \"sfmEnableCameraPrior=False\" -set \"sfmMaxFeaturesPerMpx=20000\" -set \"sfmMaxFeaturesPerImage=200000\" -set \"sfmImagesOverlap=High\" -set \"sfmMaxFeatureReprojectionError=1\" -addFolder "D:\\{pid}\\photo1" -addFolder "D:\\{pid}\\photo2" -importControlPointsMeasurements "D:\\{pid}\\{pid}..controlPoints.csv" -align -save' \ No newline at end of file diff --git a/suwa_cut_foot_cut_normal_data.txt b/suwa_cut_foot_cut_normal_data.txt new file mode 100644 index 0000000..678ca37 --- /dev/null +++ b/suwa_cut_foot_cut_normal_data.txt @@ -0,0 +1,1028 @@ +24940 +68906 +88630 +88577 +97498 +71421 +87608 +8171 +78384 +6997 +86851 +98679 +86508 +89740 +85599 +94736 +78354 +77972 +78376 +16920 +86395 +10074 +99642 +11797 +49366 +84827 +85974 +44784 +99994 +40869 +55887 +82481 +89063 +88616 +86500 +70532 +98474 +94109 +15707 +87872 +85838 +90422 +86171 +11997 +115116 +113276 +95446 +86246 +88628 +47645 +82455 +117658 +70740 +83983 +89694 +87049 +87163 +33899 +90469 +87975 +86075 +29805 +128193 +87550 +85362 +86502 +86330 +90227 +115220 +89919 +90425 +104867 +86584 +90132 +113019 +13073 +10873 +12171 +86890 +90903 +24772 +136305 +79333 +87745 +89431 +86146 +124091 +49934 +50221 +87912 +87084 +87516 +86424 +88116 +97615 +85963 +94817 +82435 +41537 +44781 +89199 +89727 +62516 +87565 +87268 +115194 +82190 +89120 +89563 +65653 +47053 +88046 +92424 +88481 +86349 +53638 +88617 +88445 +88205 +88355 +59322 +90448 +18252 +78351 +29507 +86428 +89783 +86035 +46352 +90406 +90843 +7126 +90148 +86279 +101042 +73988 +88836 +90369 +79755 +86673 +33247 +88524 +39732 +43054 +42595 +87737 +87271 +87451 +80007 +88221 +88686 +86275 +71321 +98878 +71422 +86497 +107527 +52884 +88241 +90589 +83068 +90447 +87596 +29796 +87439 +25048 +87168 +29888 +24941 +68508 +90209 +20197 +86443 +94857 +64004 +87848 +88369 +86496 +88590 +30033 +88475 +87861 +88253 +88983 +57762 +87416 +86552 +91177 +86734 +86490 +87954 +88772 +49404 +7814 +86425 +89596 +58122 +27534 +86347 +50160 +89579 +82700 +55026 +99989 +85710 +87095 +16089 +88591 +39662 +91180 +98714 +86419 +125464 +85715 +55030 +91183 +86383 +109374 +78448 +55730 +86435 +21780 +90151 +88255 +66521 +54023 +87765 +90139 +14475 +115123 +87079 +77145 +88178 +22210 +98993 +87015 +88503 +20203 +36791 +125436 +48477 +12177 +86278 +89957 +122998 +46130 +71303 +93778 +24766 +40776 +41320 +42781 +86445 +83980 +75405 +55370 +88882 +78149 +87649 +87514 +85939 +81419 +40943 +87901 +87235 +82486 +66531 +50007 +88374 +86709 +82496 +82511 +16076 +115114 +86833 +74624 +86857 +89107 +91185 +40884 +88509 +135848 +55011 +8547 +86112 +85794 +90052 +86078 +88918 +70756 +64088 +22152 +87635 +87290 +29427 +89364 +98674 +116852 +12172 +88827 +90439 +44779 +90088 +115132 +109445 +82483 +87818 +87873 +121685 +42047 +87052 +85912 +118440 +86503 +87175 +68349 +13084 +77142 +50370 +88563 +86495 +87037 +108109 +115122 +98091 +57168 +88507 +75408 +8610 +86949 +79125 +8776 +57204 +91173 +82696 +49327 +24942 +56654 +88359 +50027 +86300 +102719 +86446 +64142 +86611 +88627 +117356 +86454 +126409 +28921 +6970 +89024 +87887 +89621 +94967 +107145 +47890 +88093 +88995 +50342 +55018 +26695 +86338 +54400 +78385 +86820 +88968 +88573 +98996 +68697 +86331 +90551 +12335 +86511 +27553 +70729 +82408 +82571 +87906 +77221 +90600 +88629 +6968 +9023 +29770 +85788 +87390 +151860 +74411 +112909 +115378 +82125 +136885 +105856 +102508 +80095 +8164 +74708 +13024 +82623 +81145 +48218 +80514 +106158 +82563 +90603 +114365 +102402 +105871 +71141 +100610 +70831 +131590 +153149 +91115 +25043 +72000 +75773 +6983 +113196 +73463 +75572 +97938 +82044 +89072 +116130 +116192 +80466 +115416 +71842 +87966 +86513 +82793 +146462 +113922 +73438 +73711 +105016 +73412 +102759 +89036 +89971 +88871 +130972 +152817 +86507 +80759 +80706 +8141 +104590 +70200 +73444 +40947 +73470 +76324 +149080 +72970 +69739 +79145 +102141 +106243 +101876 +82100 +71758 +80770 +86906 +87477 +105343 +70279 +76230 +84057 +69633 +81580 +8140 +75622 +105490 +72923 +58586 +114159 +101076 +104459 +104628 +86672 +74833 +152986 +100841 +100025 +75045 +72792 +81921 +70599 +106421 +141436 +72135 +8124 +146818 +31596 +116457 +80996 +105102 +113518 +72976 +82434 +105171 +40913 +114432 +137257 +86995 +8200 +75689 +106671 +44786 +70475 +81564 +146161 +71404 +104561 +113937 +73392 +81532 +112984 +82748 +85998 +88038 +71089 +74118 +76108 +106324 +87291 +81026 +88947 +106337 +105794 +82705 +70888 +69593 +74327 +80415 +122616 +76396 +8253 +70885 +151841 +72077 +116187 +114307 +72260 +6963 +70596 +88712 +98868 +80312 +86746 +8199 +100005 +82282 +40948 +121880 +71508 +13121 +80683 +82181 +152309 +49325 +105360 +104984 +86989 +85502 +72199 +71100 +40816 +88272 +102239 +104128 +96956 +73389 +106465 +114158 +104528 +69644 +105650 +51431 +104418 +105344 +73597 +58190 +68680 +105439 +102687 +114697 +105104 +116598 +62515 +96436 +102542 +70455 +81528 +89710 +105088 +102753 +69898 +115929 +86115 +106738 +142193 +122293 +85774 +72306 +81554 +71067 +28769 +104130 +105979 +75466 +8121 +72095 +5875 +152573 +141587 +101283 +81261 +104849 +135470 +114587 +74989 +60247 +91151 +76329 +70638 +74981 +102295 +104957 +104164 +98858 +75392 +153167 +114890 +152766 +142029 +75523 +80002 +82209 +80822 +73382 +86491 +114166 +69258 +81891 +40814 +106078 +104489 +75411 +84820 +105702 +88514 +106753 +136880 +106066 +87307 +122608 +88545 +112911 +76105 +82123 +73125 +80407 +76014 +106220 +122735 +72975 +86065 +73474 +68786 +106129 +40876 +105802 +82428 +75508 +88593 +72337 +137362 +87354 +153039 +87155 +69892 +100718 +80127 +76237 +116521 +71539 +80519 +89530 +71209 +80743 +80209 +88184 +49361 +152218 +117178 +106310 +88006 +81729 +103216 +87615 +64126 +107188 +86128 +88363 +69714 +72203 +70355 +70530 +73472 +74368 +86509 +122599 +74410 +82382 +75609 +105768 +82204 +114197 +114035 +8160 +114335 +69639 +35645 +114821 +75491 +31191 +82752 +80518 +39410 +102707 +122205 +73744 +152421 +12089 +80470 +116398 +73799 +104680 +104090 +105620 +87996 +105287 +129944 +89201 +52051 +106177 +122650 +73924 +89570 +82139 +146823 +64996 +80812 +137281 +82722 +146064 +116528 +114299 +104391 +80510 +70527 +30034 +86860 +105699 +104122 +80037 +104458 +128547 +72248 +86073 +98860 +137373 +44775 +105390 +105251 +114203 +73217 +74383 +114565 +59610 +104870 +82614 +153162 +100015 +8145 +117625 +80998 +54403 +88128 +73437 +73356 +81920 +88417 +136185 +105697 +70889 +72957 +72798 +105035 +102468 +80208 +116546 +104933 +93707 +74685 +71461 +73976 +105426 +44755 +70349 +44794 +122619 +74027 +74687 +89184 +53139 +80362 +73128 +40781 +6847 +152106 +106473 +72070 +44836 +86482 +75052 +40772 +74560 +81985 +105876 +105872 +40925 +88682 +151995 +72783 +77974 +145908 +49345 +49357 +121936 +107178 +87188 +105039 +136716 +88327 +102747 +90704 +105445 +81824 +152098 +40771 +116999 +75374 +85743 +88804 +101599 +73257 +6991 +146466 +105273 +69771 +87506 +113497 +86325 +152423 +86680 +82792 +104109 +86694 +75019 +100675 +85944 +116503 +115524 +141188 +88936 +105609 +91175 +82512 +105750 +116176 +74197 +106625 +73722 +82082 +84064 +70300 +75272 +104911 +141385 +74925 +76101 +72419 +106094 +70336 +104460 +81258 +74097 +153236 +44788 +6965 +86684 +81211 +102194 +113844 +105642 +114395 +141141 +88852 +73738 +75501 +152182 +142162 +74784 +64155 +97494 +114781 +152500 +145528 +76106 +70120 +75377 +71840 +146105 +152498 +151840 +81701 +74702 +146669 +81450 +73291 +75446 +81590 +31599 +122287 +71129 +113123 +18505 +121624 +69765 +69160 +40882 +89268 +76339 +152775 +70255 +89076 +82749 +88239 +104140 +106382 +87864 +74679 +71551 +39413 +102313 +105728 +70171 +81476 +116582 +73045 +85612 +73820 +105007 +148909 +137185 +87283 +89413 +43059 +115968 +73451 +72205 +56408 +86706 +106597 +71633 +102466 +72763 +113774 +12088 +89584 +113944 +73467 +75788 +122605 +71093 +114190 +76328 +76387 +76213 +69770 +82046 +106083 +136017 +84730 +69133 +114523 +74074 +106342 +104548 +100735 +30336 +106086 +104142 +122523 +88697 +139439 +80290 +116574 +75114 +114416 +72223 +75518 +73939 +114359 +48250 +73049 +71535 +113667 +72169 +100824 +73262 +77232 +61802 \ No newline at end of file diff --git a/task_distributed_error.log b/task_distributed_error.log new file mode 100644 index 0000000..64b6397 Binary files /dev/null and b/task_distributed_error.log differ diff --git a/test.py b/test.py index 1498d28..c2937f0 100644 --- a/test.py +++ b/test.py @@ -1,15 +1,18 @@ -import psutil +import re -# 获取硬盘分区列表 -partitions = psutil.disk_partitions() +def extract_o_option(file_path): + pattern = re.compile(r'o\s+(\S+)') + with open(file_path, 'r') as file: + for line in file: + match = pattern.search(line) + if match: + return match.group(1) + return None -for partition in partitions: - print("分区信息",partition) - # 获取指定分区(比如'C:')的使用情况 - if partition.mountpoint == 'G:\\': # 更改为您要检测的硬盘分区 - usage = psutil.disk_usage(partition.mountpoint) - - print(f"Total: {usage.total / (1024**3):.2f} GB") - print(f"Used: {usage.used / (1024**3):.2f} GB") - print(f"Free: {usage.free / (1024**3):.2f} GB") - print(f"Percentage: {usage.percent}%") \ No newline at end of file +# 使用示例 +file_path = 'C:/Users/Administrator/Desktop/29282/29282_9cm_x1.obj' +result = extract_o_option(file_path) +if result: + print(f'Found -o option with value: {result}') +else: + print('No -o option found') diff --git a/timer/check_task.py b/timer/check_task.py index 31eca16..a2d1657 100644 --- a/timer/check_task.py +++ b/timer/check_task.py @@ -158,8 +158,8 @@ def get_time_out(hostname,step): if __name__ == '__main__': #开启死循环 while True: - check_task_distributed_detail() - check_task() + # check_task_distributed_detail() + # check_task() #两分钟检测一次 time.sleep(120) print("检测是否存在运行时间超长的任务,进行消息通知") diff --git a/timer/get_weight_to_update.py b/timer/get_weight_to_update.py new file mode 100644 index 0000000..cf854da --- /dev/null +++ b/timer/get_weight_to_update.py @@ -0,0 +1,74 @@ +import redis,time,requests,json,os + + +def getOrderInfoByOrderId(orderId): + try: + + url = "https://mp.api.suwa3d.com/api/physical/infoModelSizeByOrderId?order_id="+str(orderId) + print("url:",url) + res = requests.get(url) + if res.status_code != 200: + print('获取失败,程序退出') + return "error","error" + print("res",res) + res = json.loads(res.text) + print("res",res) + #获取到model_size + modelSize = res['data']['model_size'] + pid = res['data']['pid'] + return pid,modelSize + + except Exception as e: + print(f"获取订单信息异常: {str(e)}") + return "error","error" + +if __name__ == '__main__': + r = redis.Redis(host="106.14.158.208",password="kcV2000",port=6379,db=6) + while True: + print(f"时间-{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}") + if r.llen('model:weight') == 0: + print('队列为空,等待10秒') + time.sleep(10) + continue + + # info 的数据格式为有以下几种情况 1. orderId 2 orderId_print_9,1.2,45,27 3. orderId_auto_9,1.2,45,27 + info = r.lpop('model:weight') + if info is None: + print('队列为空,等待10秒') + time.sleep(10) + info = info.decode('utf-8') + orderId = 0 + pid = 0 + modelSize = [] + typeModel = "print" + #判断info是什么类型的数据 + arrInfo = info.split("_") + if len(arrInfo) == 1: + orderId = arrInfo[0] + pid,modelSize = getOrderInfoByOrderId(orderId) + if pid == "error": + print("获取订单信息失败") + continue + + elif len(arrInfo) == 3: + orderId = arrInfo[0] + pid,temp = getOrderInfoByOrderId(orderId) + if pid == "error": + print("获取订单信息失败") + continue + typeModel = arrInfo[1] + modelSize = arrInfo[2] + if typeModel != "print" and typeModel != "auto": + print("typeModel类型不对") + continue + + + if int(orderId) == 0 or int(pid) == 0 or len(modelSize) == 0: + print("orderId pid modelSize 有出现问题") + continue + + #转成数组 + arrModelSize = modelSize.split(",") + print(f'执行脚本--- "python D:/make2/tools/cal_weight.py {typeModel} {pid} {modelSize} {orderId}"') + #发起计算请求 + os.system(f"python D:/make2/tools/cal_weight.py {typeModel} {pid} {modelSize} {orderId}") diff --git a/timer/init_timer.py b/timer/init_timer.py new file mode 100644 index 0000000..d23cc92 --- /dev/null +++ b/timer/init_timer.py @@ -0,0 +1,8 @@ +import time,os +if __name__ == '__main__': + #开启死循环 + while True: + #检测当前打印 和 脚底板 和 gs 队列长度 + os.system("python E:\\make2\\tools\\push_cmd.py auto_view") + #一个小时检测一次 + time.sleep(60*60) \ No newline at end of file diff --git a/timer/setup_oem_exe.py b/timer/setup_oem_exe.py index 2d2b631..0221897 100644 --- a/timer/setup_oem_exe.py +++ b/timer/setup_oem_exe.py @@ -3,7 +3,7 @@ import redis,requests,json,os,sys import platform if platform.system() == 'Windows': - sys.path.append('e:\\libs\\') + sys.path.append('e:\\make2\\libs\\') else: sys.path.append('/data/deploy/make3d/make2/libs/') import config @@ -66,32 +66,32 @@ def main(): print(data['data']) #pythob 进入到 E:\wails\oemProject ,将 data 数据写入到 wails.json 文件中的 ext_oem 字段中 - with open("D:\\oemProject\\wails.json","r",encoding="utf-8") as f: + with open("E:\\make2\\oemProject\\wails.json","r",encoding="utf-8") as f: wailsData = json.load(f) wailsData['ext_oem'] = data['data'] wailsData['name'] = data['data']['brand_name'] wailsData["outputfilename"] = str(data['data']['id']) - with open("D:\\oemProject\\wails.json","w",encoding="utf-8") as f: + with open("E:\\make2\\oemProject\\wails.json","w",encoding="utf-8") as f: json.dump(wailsData,f,ensure_ascii=False,indent=4) # #将图片下载到本地 - getExeImg(data['data']['logo'], "D:\\oemProject\\build\\appicon.png") + getExeImg(data['data']['exe_logo'], "E:\\make2\\oemProject\\build\\appicon.png") #删除 D:\\oemProject\\build\\bin 目录下的所有exe 文件,不包含子目录 - delete_exe_files_in_dir("D:\\oemProject\\build\\bin") + delete_exe_files_in_dir("E:\\make2\\oemProject\\build\\bin") # #重命名文件 # os.system(f"cd D:\\oemProject && ren build\\bin\\{data['data']['brand_name']}amd64-installer.exe "+str(data['data']['id'])+".exe") #写入成功后在 E:\\wails\\oemProject\\ 目录下执行 wails build 命令 -debug - os.system("cd D:\\oemProject && wails build -nsis") + os.system("cd E:\\make2\\oemProject && wails build -nsis -devtools") time.sleep(35) #删除指定目录下的多余产生的文件 - os.system("cd D:\\oemProject && del /s/q/f build\\bin\\"+str(data['data']['id'])+".exe") + os.system("cd E:\\make2\\oemProject && del /s/q/f build\\bin\\"+str(data['data']['id'])+".exe") #打包压缩指定文件夹 # 指定要打包的文件夹路径 - folder_to_zip = 'D:\\oemProject\\build\\bin' + folder_to_zip = 'E:\\make2\\oemProject\\build\\bin' # 指定生成的压缩包名称和路径 zip_filename = f"{str(data['data']['id'])}.zip" @@ -104,7 +104,7 @@ def main(): #上传成功后,调用接口,告诉mp,已经生成exe及上传成功 requests.get(url+"/api/oem/infoSetupUpdate",params={"id":oemId}) # - os.system("cd D:\\make2 && del /s/q/f timer\\"+str(data['data']['id'])+".zip") + os.system("cd E:\\make2 && del /s/q/f timer\\"+str(data['data']['id'])+".zip") def getExeImg(image_url, output_path): @@ -118,10 +118,10 @@ def getExeImg(image_url, output_path): # 保存图片为PNG格式 resized_image.save(output_path, 'PNG') #转换格式 - transToIco(output_path,"D:\\oemProject\\build\\windows\\icon.ico") + transToIco(output_path,"E:\\make2\\oemProject\\build\\windows\\icon.ico") #复制图片到指定目录 time.sleep(3) - shutil.copy("D:\\oemProject\\build\\windows\\icon.ico","D:\\oemProject\\build\\bin\\icon.ico",) + shutil.copy("E:\\make2\\oemProject\\build\\windows\\icon.ico","E:\\make2\\oemProject\\build\\bin\\icon.ico",) else: print(f"Failed to download image. Status code: {response.status_code}") diff --git a/tools/base_data_fix.py b/tools/base_data_fix.py new file mode 100644 index 0000000..fae4a09 --- /dev/null +++ b/tools/base_data_fix.py @@ -0,0 +1,102 @@ +import platform,sys,redis,time,requests,json,atexit +sys.path.append('../libs/') + +import config,libs,libs_db_temp,common + +import os, sys, time, argparse, requests, json, re, oss2, probreg +import bpy, bmesh +import open3d as o3d +import numpy as np +import cupy as cp +from mathutils import Matrix +import math +import logging +import matplotlib.pyplot as plt + +#获取真实身高 +def get_real_height(input_path): + get_real_height_url = 'https://mp.api.suwa3d.com/api/physical/infoByPid' + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Getting real height from {input_path}') + pid = re.findall(r'(\d+)', input_path)[0] + print(f'pid: {pid}') + res = requests.get(get_real_height_url, params={'pid': pid}) + res = json.loads(res.text) + if res['code'] == -1: + print(f'Error: {res["message"]}, return default height 160') + return 160 + height = res['data']['height'] + if height == 0: + print(f'Error: height=0, return default height 160') + return 160 + print(f'height: {height}') + return height + + +def base_fix(input_path): + if not os.path.exists(input_path): + print(f'Error: {input_path} does not exist.') + sys.exit(1) + + bpy.ops.wm.read_homefile() + bpy.ops.object.delete(use_global=False, confirm=False) + bpy.ops.wm.obj_import(filepath=input_path) + bpy.context.scene.unit_settings.scale_length = 1 + bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS' + bpy.context.scene.unit_settings.mass_unit = 'GRAMS' + + obj = bpy.context.selected_objects[0] + bpy.context.view_layer.objects.active = obj + obj.select_set(True) + + # 脚底贴地 + bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Z'}) + bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) + + real_height = get_real_height(input_path) + print(obj.dimensions) + scale = real_height / obj.dimensions[2] / 100 # 除以100是因为单位是厘米 + bpy.context.object.scale = (scale, scale, scale) + bpy.context.object.rotation_euler = (0, 0, 0) + bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN') + bpy.context.object.location[0] = 0 # 移动到特定位置(1m, 1m)是为了让human3d可以正确识别 + bpy.context.object.location[1] = 0 + bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) + + # # bpy.ops.wm.save_mainfile(filepath=args.input_path.replace('.obj', '.blend')) + output_file = input_path.replace('.obj', '_baseFixed.obj') + bpy.ops.wm.obj_export(filepath=output_file) + return obj + + +def readTask(): + # #读取数据库的任务 + # pid = libs_db_temp.get_task_by_level() + # pid = str(pid) + # #下载pid的对应的oss端的文件 + # tempName = libs.down_obj_from_oss_for_fix("E://obj_fix", pid, "print") + # if tempName == "": + # print("找不到对应的obj文件") + # #记录状态为异常 -1 + # libs_db_temp.update_fix_status(pid,-1) + # return "" + + # #记录状态为处理中 + # libs_db_temp.update_fix_status(pid,1) + # #处理逻辑 + # return pid + pid=7013 + + base_fix(f'E://obj_fix/{pid}/{pid}.obj') + + # print(pid) + # print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())+"-读取数据库任务") + +#程序主入口 +if __name__ == '__main__': + #atexit.register(common.notify,"处理数据校准任务已经停止") + readTask() + # while True: + # print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())+"-开始执行任务") + # pid = readTask() + # if pid != "": + # break \ No newline at end of file diff --git a/tools/cal_weight.py b/tools/cal_weight.py index 7ac9f76..0194643 100644 --- a/tools/cal_weight.py +++ b/tools/cal_weight.py @@ -1,4 +1,4 @@ -import os, sys, time, bpy, bmesh, shutil +import os, sys, time, bpy, bmesh, shutil,requests,json import platform if platform.system() == 'Windows': sys.path.append('e:\\libs\\') @@ -64,16 +64,35 @@ def cal_weight(obj, size): model_info['volume'] = round(bm.calc_volume() / 1000) model_info['weight'] = round(model_info['volume'] * 1.226) print(f'{size/10}cm:体积 {model_info["volume"]}cm³, 克重 {model_info["weight"]}g') + return model_info["weight"] -def main(action, pid, sizes): +def main(action, pid, sizes,orderId=0): libs.down_obj_from_oss(config.workdir, pid, action) obj = reload_obj(pid, action) bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) print(f'模型{pid}的体积与克重估算信息:') + arrData = {} for size in sizes: size = float(size) - cal_weight(obj, size) + weight = cal_weight(obj, size) + size = str(size/10)+"cm" + arrData[size] = str(weight)+"g" + + #请求接口进行更新数据 + arrParams = {"pid":pid,"order_id":orderId} + if action == "auto": + arrParams["auto_weight"] = json.dumps(arrData) + + if action == "print": + arrParams["print_weight"] = json.dumps(arrData) + + #发起请求 + print(f"请求的参数-{arrParams}") + url = "https://mp.api.suwa3d.com/api/physical/addPhysicalWeight" + res = requests.post(url,data=arrParams) + print('res:', res.text) + if __name__ == '__main__': sizes = (90, 120, 150, 180) @@ -83,10 +102,19 @@ if __name__ == '__main__': pids = sys.argv[2].split(',') for pid in pids: main(action, pid, sizes) + elif len(sys.argv) == 5: + action = sys.argv[1] + pids = sys.argv[2].split(',') + sizes = sys.argv[3].split(',') + orderId = sys.argv[4] + for pid in pids: + main(action, pid, sizes,orderId) + print('Usage: python cal_weight.py print order_id 自定义尺寸/默认四个尺寸就不填') + elif len(sys.argv) == 4: action = sys.argv[1] pids = sys.argv[2].split(',') sizes = sys.argv[3].split(',') for pid in pids: main(action, pid, sizes) - print('Usage: python cal_weight.py print 自定义尺寸/默认四个尺寸就不填') \ No newline at end of file + print('Usage: python cal_weight.py print order_id 自定义尺寸/默认四个尺寸就不填') \ No newline at end of file diff --git a/tools/get_weight_by_pid.py b/tools/get_weight_by_pid.py new file mode 100644 index 0000000..002fb97 --- /dev/null +++ b/tools/get_weight_by_pid.py @@ -0,0 +1,43 @@ +import os,sys,requests,json + + +if __name__ == "__main__": + #获取参数 + args = sys.argv + print("args:",args) + if len(args) != 2: + print("参数错误") + exit() + + pid = args[1] + print("pid",pid) + #根据pid查询order_id的集合 + url="https://mp.api.suwa3d.com/api/physical/listOrderIdByPid?pid="+str(pid) + res=requests.get(url) + if res.status_code != 200: + print('获取失败,程序退出') + exit() + res = json.loads(res.text) + print("res",res) + if res["code"] != 1000: + exit() + orderIds = res['data'] + print("orderIds:",orderIds) + #循环处理 + for orderId in orderIds: + #os.system(f"python D:/make2/tools/cal_weight.py print {pid} {orderId}") + url = "https://mp.api.suwa3d.com/api/physical/infoModelSizeByOrderId?order_id="+str(orderId) + print("url:",url) + res = requests.get(url) + if res.status_code != 200: + print('获取失败,程序退出') + continue + print("res",res) + res = json.loads(res.text) + print("res",res) + #获取到model_size + modelSize = res['data']['model_size'] + pid = res['data']['pid'] + print(f"执行脚本 python D:/make2/tools/cal_weight.py auto {pid} {modelSize} {orderId}") + os.system(f"python D:/make2/tools/cal_weight.py auto {pid} {modelSize} {orderId}") + # python D:/make2/tools/get_weight_by_pid.py \ No newline at end of file diff --git a/tools/optimize_model/README.txt b/tools/optimize_model/README.txt new file mode 100644 index 0000000..69e4ccd --- /dev/null +++ b/tools/optimize_model/README.txt @@ -0,0 +1,6 @@ +1. pip install -r requirements.txt安装requirements.txt所需的python包 (python3.10) +2. 使用python optimize_model.py -ip 输入的obj路径 -op 输出的obj路径 + + + +python optimize_model.py -ip "D:\finished\190104\output\190104.obj" -op "D:\finished\190104\output\190104_new.obj" \ No newline at end of file diff --git a/tools/optimize_model/libs.py b/tools/optimize_model/libs.py new file mode 100644 index 0000000..d8950b1 --- /dev/null +++ b/tools/optimize_model/libs.py @@ -0,0 +1,381 @@ +import os, time, json, requests, shutil, oss2, psutil +from tqdm import tqdm +from PIL import Image, ImageEnhance +import config,libs_db,common +import threading +from concurrent.futures import ThreadPoolExecutor + +def find_blender_bin_path(): + base_path = 'C:\\Program Files\\Blender Foundation\\' + if os.path.exists(base_path): + for dir in os.listdir(base_path): + if dir.startswith('Blender'): + blender_bin_path = base_path + dir + '\\blender.exe' + return f'"{blender_bin_path}"' + else: + print('未找到blender安装目录') + exit(1) + +def resize_photos(photo_path, ratio=0.5): + for filename in os.listdir(photo_path): + if filename.endswith('.jpg'): + img = Image.open(os.path.join(photo_path, filename)) + img = rotate_image(img) + w, h = img.size + img = img.resize((int(w * ratio), int(h * ratio))) + img.save(os.path.join(photo_path, filename)) + +def rotate_image(image): + # 检查图像的EXIF数据是否包含方向信息 + try: + exif = image._getexif() + orientation = exif.get(0x0112) + except: + orientation = None + + # 根据方向信息旋转图像 + if orientation == 3: + image = image.rotate(180, expand=True) + elif orientation == 6: + image = image.rotate(270, expand=True) + elif orientation == 8: + image = image.rotate(90, expand=True) + return image + +def get_ps_adjust_photo_para(psid): + res = requests.get(config.urls['get_ps_adjust_photo_para_url'], params={'id': psid}) + print(res.json()) + paras = res.json()['data'] + brightness_factor, saturation_factor, temperature_factor = float(paras['brightness']), float(paras['saturation']), float(paras['colorTemperature']) + return brightness_factor, saturation_factor, temperature_factor + +def adjust_photos(workdir, pid): + def adjust_brightness(image, brightness_factor): + if brightness_factor == 1 or brightness_factor == 0 : + return image + enhancer = ImageEnhance.Brightness(image) + adjusted_image = enhancer.enhance(brightness_factor) + return adjusted_image + + def adjust_saturation(image, saturation_factor): + if saturation_factor == 1: + return image + enhancer = ImageEnhance.Color(image) + adjusted_image = enhancer.enhance(saturation_factor) + return adjusted_image + + def adjust_temperature(image, temperature_factor): + if temperature_factor == 1: + return image + r, g, b = image.split() + r = r.point(lambda i: i * temperature_factor) + adjusted_image = Image.merge("RGB", (r, g, b)) + return adjusted_image + if not os.path.exists(os.path.join(workdir, pid, 'photo2')): + print(f"Directory {os.path.join(workdir, pid, 'photo2')} does not exist") + return False + + psid = getPSid(pid) + brightness_factor, saturation_factor, temperature_factor = get_ps_adjust_photo_para(psid) + + if (brightness_factor == 1 and saturation_factor == 1 and temperature_factor == 1): + print("No need to adjust") + return False + if os.path.exists(os.path.join(workdir, pid, 'photo3')): + print(f'{os.path.join(workdir, pid, "photo3")}目录已存在,跳过') + return + os.makedirs(os.path.join(workdir, pid, 'photo3'), exist_ok=True) + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 开始调整图片曝光...') + start_time = time.time() + for filename in os.listdir(os.path.join(workdir, pid, 'photo2')): + if filename.endswith(".jpg"): + # print(f"Adjusting {filename}:brightness={brightness_factor}, saturation={saturation_factor}, temperature={temperature_factor}") + image = Image.open(os.path.join(workdir, pid, 'photo2', filename)) + image = rotate_image(image) + + brightened_image = adjust_brightness(image, brightness_factor) + saturated_image = adjust_saturation(brightened_image, saturation_factor) + adjusted_image = adjust_temperature(saturated_image, temperature_factor) + + adjusted_image.save(os.path.join(workdir, pid, 'photo3', filename)) + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 图片曝光调整完成,共费时{diff_time(start_time)}') + return True + +def getPSid(pid): + res = requests.get(config.urls['get_psid_url'], params={'pid': pid}) + print('get_psid_url:', res.url) + print('res:', res.text) + res = json.loads(res.text) + return str(res['data']) + +def getHeadCount(pid): + res = requests.get(config.urls['get_printinfo_url'], params={'id': pid}) + print('get_printinfo_url:', res.url) + print('res:', res.text) + if res.status_code != 200: + print('获取人数失败,程序退出') + exit(1) + res = json.loads(res.text) + return res['data']['headcount'] + +def get_ps_type(pid): + # return 1:圆形影棚 2:方形影棚 + res = requests.get(config.urls['get_ps_type_url'], params={'pid': pid}) + return res.json()['data']['type'] + +def find_valid_camera_on_oss(pid): + if get_ps_type(pid) == 1: + print('当前拍照影棚为:圆形影棚') + cameras = (103, 93, 113) + else: + print('当前拍照影棚为:方形影棚') + cameras = (74, 64, 84) + find_camera = 0 + for camera in cameras: + objectkey = f'photos/{pid}/photo2/{camera}_8.jpg' + find = config.oss_bucket.object_exists(objectkey) + if find: + find_camera = camera + break + + print('找到有效正脸相机:', find_camera) + if find_camera == 0: + print('{cameras}没有找到照片,程序退出') + exit(1) + return find_camera + +def aliyun_face(pid): + high = False + style = 'imm/detectface' + camera = find_valid_camera_on_oss(pid) + objectkey = f'photos/{pid}/photo2/{camera}_8.jpg' + try: + res = config.oss_bucket.get_object(objectkey, process=style) + except oss2.exceptions.NoSuchKey: + print('没有找到文件:', objectkey) + return high + res = json.loads(res.read()) + if res['success']: + if res['Faces'] is None: + print('no face') + return None + else: + print('faces num:', len(res['Faces'])) + for face in res['Faces']: + print('-' * 20) + print('face_id:', face['FaceId']) + print('gender:', face['Gender']) + print('age:', face['Age']) + + if face['Gender'] == 'FEMALE' and face['Age'] < 22: high = True + if face['Gender'] == 'MALE' and face['Age'] < 15: high = True + else: + print('face detect failed...') + return high + +def down_obj_from_oss(workdir, pid, action): + if os.path.exists(os.path.join(workdir, action, pid)): + print(f'目录{os.path.join(workdir, action, pid)}已存在,跳过') + return + else: + os.makedirs(os.path.join(workdir, action, pid)) + + # 根据前缀获取文件列表 + prefix = f'objs/{action}/{pid}/' + filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix) + print('正在下载:', prefix) + obj_filename = "" + for file in filelist: + filename = file.key.split('/')[-1] + if filename.endswith('.obj'): + obj_filename = filename + # print('正在下载:', file.key) + localfile = os.path.join(workdir, action, pid, filename) + config.oss_bucket.get_object_to_file(file.key, localfile) + + return obj_filename + +def set_photos_join_type(workdir, pid, photoN, mesh = '0', texture='0'): + photoN_path = os.path.join(workdir, pid, photoN) + for xmp in os.listdir(photoN_path): + if xmp.endswith('.xmp'): + xmp_path = os.path.join(photoN_path, xmp) + with open(xmp_path, 'r') as f: + lines = f.readlines() + lines = [line.replace('xcr:InMeshing="0"', f'xcr:InMeshing="{mesh}"') for line in lines] + lines = [line.replace('xcr:InMeshing="1"', f'xcr:InMeshing="{mesh}"') for line in lines] + lines = [line.replace('xcr:InTexturing="0"', f'xcr:InTexturing="{texture}"') for line in lines] + lines = [line.replace('xcr:InTexturing="1"', f'xcr:InTexturing="{texture}"') for line in lines] + with open(xmp_path, 'w') as f: + f.writelines(lines) + +def set_photo_join_type(workdir, pid, photoN, camera_id, mesh = '0', texture='0'): + if photoN == 'photo1': + filename = os.path.join(workdir, pid, photoN, f'{camera_id}_1.xmp') + else: + filename = os.path.join(workdir, pid, photoN, f'{camera_id}_8.xmp') + with open(filename, 'r') as f: + lines = f.readlines() + lines = [line.replace('xcr:InMeshing="0"', f'xcr:InMeshing="{mesh}"') for line in lines] + lines = [line.replace('xcr:InMeshing="1"', f'xcr:InMeshing="{mesh}"') for line in lines] + lines = [line.replace('xcr:InTexturing="0"', f'xcr:InTexturing="{texture}"') for line in lines] + lines = [line.replace('xcr:InTexturing="1"', f'xcr:InTexturing="{texture}"') for line in lines] + with open(filename, 'w') as f: + f.writelines(lines) + +def down_from_oss(oss_client, workdir, pid, per=100,photoPath=""): + start_time = time.time() + path = os.path.join(workdir, pid) + if os.path.exists(path): + print(f"Directory {path} already exists, skip") + return + os.makedirs(os.path.join(path, 'photo1')) + os.makedirs(os.path.join(path, 'photo2')) + + psid = getPSid(pid) + # 根据前缀获取文件列表 + prefix = f'photos/{pid}/' + filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) + for file in tqdm(filelist): + filename = file.key.split('/')[-1] + localfile = "" + # print('正在下载:', file.key) + if photoPath == "": + if filename.endswith('_1.jpg'): + localfile = os.path.join(path, 'photo1', filename) + else: + localfile = os.path.join(path, 'photo2', filename) + else: + if photoPath=="1": + if filename.endswith('_1.jpg'): + localfile = os.path.join(path, 'photo1', filename) + else: + if filename.endswith('_8.jpg'): + localfile = os.path.join(path, 'photo2', filename) + if localfile == "": + continue + style = f'image/resize,p_{per}' + if per == 100: + oss_client.get_object_to_file(file.key, localfile) + else: + oss_client.get_object_to_file(file.key, localfile, process=style) + + #判断localfile 是否有包含 photo2 + + + #遍历处理photo2的数数据 + # if str(psid) == "96" or str(pid) == "118994": + # path = os.path.join(workdir, pid, 'photo2') + # files = [] + # for fileName in os.listdir(path): + # if ".jpg" in fileName: + # files.append(path+"\\"+fileName) + # beginTime = time.time() + # with ThreadPoolExecutor(max_workers=6) as executor: + # executor.map(process_image, files) + + # print(f'{localfile}灰度处理费时{diff_time(beginTime)}') + + + print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 图片下载完成, 共费时{diff_time(start_time)}') + +#灰度处理图片 +def process_image(localfile): + if ".jpg" in localfile: + common.remove_gray_and_sharpening(localfile) + +def get_defineDistances(psid): + res = '' + distances = libs_db.get_floor_sticker_distances(psid).split(';') + print("distances",distances) + for d in distances: + p1, p2, distance = d.split(' ') + res = res + f' -defineDistance {p1} {p2} {distance}' + return res.strip() + +def is_running(psname): + for p in psutil.process_iter(['name']): + if psname.strip() in p.info['name']: + return True + return False + +def diff_time(start_time): + # 按照分:秒的方式返回时间差 + end_time = time.time() + diff = end_time - start_time + m, s = divmod(diff, 60) + return f'{int(m)}分{int(s)}秒' + +def down_obj_from_oss_for_fix(workdir, pid, action): + if os.path.exists(os.path.join(workdir, action, pid)): + print(f'目录{os.path.join(workdir, action, pid)}已存在,跳过') + return + else: + os.makedirs(os.path.join(workdir, action, pid)) + + # 根据前缀获取文件列表 + prefix = f'objs/{action}/{pid}/' + filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix) + print('正在下载:', prefix) + obj_filename = "" + for file in filelist: + filename = file.key.split('/')[-1] + if filename.endswith(f'{pid}.obj') == False: + continue + + if filename.endswith('.obj'): + obj_filename = filename + + # print('正在下载:', file.key) + localfile = os.path.join(workdir, action, pid, filename) + config.oss_bucket.get_object_to_file(file.key, localfile) + return obj_filename + +def down_obj_from_oss_for_repeat_texture(workdir, pid, action): + print(os.path.join(workdir, pid)) + if not os.path.exists(os.path.join(workdir, pid)): + print("目录不存在,无法处理") + return "error" + + #判断是否有专门新建的目录用来重新贴图 + newPath = os.path.join(workdir, pid,'repeat_texture','completion_cloud') + if not os.path.exists(newPath): + os.makedirs(newPath) + + + # 根据前缀获取文件列表 + prefix = f'objs/{action}/{pid}/ai/repeat_texture/completion_cloud' + filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix) + print('正在下载:', prefix) + for file in filelist: + filename = file.key.split('/')[-1] + if filename == "": + continue + localfile = os.path.join(newPath, filename) + config.oss_bucket.get_object_to_file(file.key, localfile) + return newPath + + + + +def down_obj_from_oss_auto(pid,action,pathStr): + #pathStr = os.path.join(workdir, str(pid)+"_ai") + if not os.path.exists(pathStr): + os.makedirs(pathStr) + + # 根据前缀获取文件列表 + prefix = f'objs/{action}/{pid}' + filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix) + print('正在下载:', prefix) + for file in filelist: + filename = file.key.split('/')[-1] + if filename == "": + continue + + if filename != f"{pid}.jpg" and filename != f"{pid}.obj" and filename != f"{pid}.mtl": + continue + + print(filename) + localfile = os.path.join(pathStr, filename) + config.oss_bucket.get_object_to_file(file.key, localfile) \ No newline at end of file diff --git a/tools/optimize_model/main.py b/tools/optimize_model/main.py new file mode 100644 index 0000000..6313a68 --- /dev/null +++ b/tools/optimize_model/main.py @@ -0,0 +1,31 @@ +import paramiko,time,sys + +def main_optimize_model(pid): + try: + # 创建SSH对象 + ssh = paramiko.SSHClient() + # 允许连接不在known_hosts文件中的主机 + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + print('start connect') + # 连接服务器 + ssh.connect('connect.bjc1.seetacloud.com', username='root', password='QoQA8q3Ds2VB', port=33733) + print('connect success') + + # 使用nohup执行命令,并将输出重定向到文件 + command = f'nohup seg_python /data/code/optimize_model_xj/optimize_model.py -pid {pid} >{pid}.log 2>&1&' + stdin, stdout, stderr = ssh.exec_command(command) + + # 打印命令是否启动成功 + print("Command executed") + finally: + # 确保连接被关闭 + if ssh: + time.sleep(1) + ssh.close() + +if __name__ == '__main__': + + if len(sys.argv) == 2: + pid = sys.argv[1] + main_optimize_model(pid) + diff --git a/tools/optimize_model/optimize_model.py b/tools/optimize_model/optimize_model.py new file mode 100644 index 0000000..e7dd005 --- /dev/null +++ b/tools/optimize_model/optimize_model.py @@ -0,0 +1,244 @@ +import bpy +import sys +import open3d as o3d +import numpy as np +from sklearn.neighbors import NearestNeighbors +import os +import argparse + +def estimate_point_curvatures(point_cloud, k=30): + point_cloud_points = np.asarray(point_cloud.points) + # Initialize the nearest neighbors finder + nn = NearestNeighbors(n_neighbors=k) + nn.fit(point_cloud_points) + curvatures = {i: 0 for i in range(len(point_cloud.points))} + for i, point in enumerate(point_cloud_points): + # Find the k-nearest neighbors (including the point itself) + distances, indices = nn.kneighbors([point]) + neighbors = point_cloud_points[indices[0]] + # Compute the covariance matrix + covariance_matrix = np.cov(neighbors - neighbors.mean(axis=0), rowvar=False) + # Compute the eigenvalues (sorted) + eigenvalues, _ = np.linalg.eigh(covariance_matrix) + eigenvalues.sort() + # Calculate curvature using the smallest eigenvalue + curvature = eigenvalues[0] / sum(eigenvalues) + curvatures[i] = curvature + return curvatures + +def compute_adjacent_normal_angle_variance(pcd, pcd_normal, distance_scale): + kdtree = o3d.geometry.KDTreeFlann(pcd) + distances = [] + for i in range(len(pcd.points)): + [k, idx, dist] = kdtree.search_knn_vector_3d(pcd.points[i], 2) + dist = dist[1:] # distances to the actual neighbors + distances.append(dist) + mean_distance = np.mean(distances) + adjacent_normal_angles_variance = {i: 0 for i in range(len(np.asarray(pcd.points)))} + for i, point in enumerate(pcd.points): + # Search for the k-nearest neighbors of the point, up to 1500 + k, idx, dist = kdtree.search_knn_vector_3d(point, 200) + idx = np.asarray(idx) + indices = idx[dist < mean_distance * distance_scale] + current_normal = pcd_normal[i] + # Calculate dot products between the current normal and its neighbors' normals + dot_products = np.dot(pcd_normal[indices], current_normal) + # Handle any potential NaN values in dot products + valid_dot_products = np.isfinite(dot_products) + dot_products = dot_products[valid_dot_products] + # Clip dot product values to valid range for arccos + dot_products = np.clip(dot_products, -1.0, 1.0) + # Compute angles in degrees and adjust for any NaNs which might be due to numerical errors + angles_array = np.degrees(np.arccos(dot_products)) + # Set the angle with itself to zero since it's not meaningful + angles_array[indices == i] = 0 + # Calculate the mean angle excluding the zero for the current point + valid_angles = angles_array[indices != i] + if valid_angles.size > 0: + adjacent_normal_angles_variance[i] = np.std(valid_angles) + else: + adjacent_normal_angles_variance[i] = 0 # Default to 0 if no valid neighbors exist + return adjacent_normal_angles_variance + +def compute_adjacent_point_vector_degrees_mean(pcd, pcd_normal, distance_scale): + kdtree = o3d.geometry.KDTreeFlann(pcd) + distances = [] + for i in range(len(pcd.points)): + [k, idx, dist] = kdtree.search_knn_vector_3d(pcd.points[i], 2) + dist = dist[1:] # distances to the actual neighbors + distances.append(dist) + mean_distance = np.mean(distances) + adjacent_point_vector_degrees_mean = {i: 0 for i in range(len(np.asarray(pcd.points)))} + for i, point in enumerate(pcd.points): + # Search for the k-nearest neighbors of the point, up to 200 + k, idx, dist = kdtree.search_knn_vector_3d(point, 200) + idx = np.asarray(idx) + indices = idx[dist < mean_distance * distance_scale] + indices = indices[indices != i] + # Retrieve the normal of the current point + current_normal = pcd_normal[i] + adjacent_point_vector = np.asarray(pcd.points)[indices] - point + adjacent_point_vector_cos = np.dot(adjacent_point_vector, current_normal) / np.linalg.norm(adjacent_point_vector, axis = 1) * 1 + valid_adjacent_point_vector_cos = np.isfinite(adjacent_point_vector_cos) + adjacent_point_vector_cos = adjacent_point_vector_cos[valid_adjacent_point_vector_cos] + if len(adjacent_point_vector_cos) > 0: + adjacent_point_vector_degrees = np.degrees(np.arccos(adjacent_point_vector_cos)) + adjacent_point_vector_mean_degrees = np.mean(adjacent_point_vector_degrees) + adjacent_point_vector_degrees_mean[i] = adjacent_point_vector_mean_degrees + else: + adjacent_point_vector_degrees_mean[i] = 0 + return adjacent_point_vector_degrees_mean + +def spread_point(pcd, selected_index, distance_scale, count = False): + kdtree = o3d.geometry.KDTreeFlann(pcd) + distances = [] + for i in range(len(pcd.points)): + [k, idx, dist] = kdtree.search_knn_vector_3d(pcd.points[i], 2) + dist = dist[1:] # distances to the actual neighbors + distances.append(dist) + mean_distance = np.mean(distances) + if count is False: + spread_selected_index = set() + for i in selected_index: + k, idx, dist = kdtree.search_knn_vector_3d(pcd.points[i], 200) + idx = np.asarray(idx) + dist = np.asarray(dist) + indices = idx[dist < mean_distance * distance_scale] + spread_selected_index.update(indices) + spread_selected_index = np.array(list(spread_selected_index)) + else: + spread_selected_index = {i: 0 for i in range(len(np.asarray(pcd.points)))} + for i in selected_index: + k, idx, dist = kdtree.search_knn_vector_3d(pcd.points[i], 200) + idx = np.asarray(idx) + dist = np.asarray(dist) + indices = idx[dist < mean_distance * distance_scale] + for indice in indices: + spread_selected_index[indice] += 1 + return spread_selected_index + +def ApplySmoothEffort(object, selected_vertice_indices = None, factor = 0.5, iterations = 30): + if selected_vertice_indices is not None: + # Assuming 'mesh' is already defined, e.g., mesh = bpy.context.object.data + selected_vertices_index = set() # Initialize an empty set to store unique vertex indices + # Iterate over each index in the list of polygon indices + for idx in selected_vertice_indices: + polygon = object.data.polygons[idx] + for vert_idx in polygon.vertices: + selected_vertices_index.add(vert_idx) + # Switch to Edit mode + bpy.ops.object.mode_set(mode='EDIT') + # Ensure we're dealing with the latest data + bpy.ops.object.mode_set(mode='OBJECT') + for i, vertex in enumerate(object.data.vertices): + vertex.select = False + if i in selected_vertices_index: + vertex.select = True + # Update the object to reflect changes + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.object.mode_set(mode='OBJECT') + # Add a Smooth modifier and set it to use the vertex group + smooth_mod = object.modifiers.new(name="SmoothMod", type='SMOOTH') + if selected_vertice_indices is not None: + # Create a new vertex group + group = object.vertex_groups.new(name='Smooth Group') + # Add selected vertices to the vertex group + group.add([v.index for v in object.data.vertices if v.select], weight=1.0, type='ADD') + smooth_mod.vertex_group = 'Smooth Group' + smooth_mod.factor = factor + smooth_mod.iterations = iterations + # bpy.ops.object.modifier_apply(modifier="SmoothMod") + # if selected_vertice_indices is not None: + # object.vertex_groups.clear() + +def process(input_mesh_path, output_mesh_path): + # Select and delete all objects in the current scene to start fresh + bpy.ops.object.select_all(action='SELECT') + bpy.ops.object.delete(confirm=False) + # Path to the OBJ file to be imported + bpy.ops.wm.obj_import(filepath=input_mesh_path) + # Set the current object to the newly imported mesh + current_obj = bpy.context.object + # Retrieve the mesh data from the current object + mesh_data = current_obj.data + bpy.context.view_layer.update() + # Convert Blender mesh vertices to a NumPy array + vertices_array = np.array([vertex.co[:] for vertex in mesh_data.vertices]) + # Convert Blender mesh polygons to a NumPy array of triangles + triangles_array = np.array([polygon.vertices[:] for polygon in mesh_data.polygons]) + + # Create an Open3D mesh object from the arrays + open3d_mesh = o3d.geometry.TriangleMesh() + open3d_mesh.vertices = o3d.utility.Vector3dVector(vertices_array) + open3d_mesh.triangles = o3d.utility.Vector3iVector(triangles_array) + open3d_mesh.compute_triangle_normals() + # Retrieve and store the normals as a NumPy array + triangle_normals_array = np.asarray(open3d_mesh.triangle_normals) + + # Compute centroids for each face of the mesh + triangle_centroids_array = np.array([ + np.mean(vertices_array[triangle], axis=0) + for triangle in open3d_mesh.triangles + ]) + # Create a point cloud of face centroids + pcd_triangle_centroid = o3d.geometry.PointCloud() + pcd_triangle_centroid.points = o3d.utility.Vector3dVector(triangle_centroids_array) + + point_curvatures = estimate_point_curvatures(pcd_triangle_centroid, 30) + np_point_curvatures = np.array(list(point_curvatures.values())) + point_curvatures_index = np.arange(len(np_point_curvatures))[np_point_curvatures > 0.25] + + spread_point_curvatures_index = spread_point(pcd_triangle_centroid, point_curvatures_index, 50) + + ApplySmoothEffort(current_obj, spread_point_curvatures_index, 0.5, 30) + + adjacent_normal_angle_variance = compute_adjacent_normal_angle_variance(pcd_triangle_centroid, triangle_normals_array, 40) + adjacent_point_vector_degrees_mean = compute_adjacent_point_vector_degrees_mean(pcd_triangle_centroid, triangle_normals_array, 40) + + array_adjacent_point_vector_degrees_mean = np.array(list(adjacent_point_vector_degrees_mean.values())) + array_adjacent_normal_angle_variance = np.array(list(adjacent_normal_angle_variance.values())) + + use_data = np.where(array_adjacent_point_vector_degrees_mean < 90, array_adjacent_normal_angle_variance, 0) + # Calculate the minimum and maximum values in the angle array, ignoring NaNs. + min_angle = np.nanmin(use_data) + max_angle = np.nanmax(use_data) + # Perform min-max normalization on the angles to scale them between 0 and 1. + # This normalization helps in comparing values on a common scale. + normalized_angles = (use_data - min_angle) / (max_angle - min_angle) + first_select_thread = normalized_angles > 0.2 + first_select_thread_number = first_select_thread.astype(np.float64) + thread_normalized_angles_index = np.arange(len(first_select_thread))[first_select_thread] + + spread_point_count = spread_point(pcd_triangle_centroid, thread_normalized_angles_index, 3, count=True) + + overlapp_trigular_points = np.array(list(spread_point_count.values())) > 3 + overlapp_trigular_points_index = np.arange(len(overlapp_trigular_points))[overlapp_trigular_points] + + ApplySmoothEffort(current_obj, overlapp_trigular_points_index, 0.5, 30) + + ApplySmoothEffort(current_obj, factor=0.5, iterations=30) + + bpy.ops.wm.obj_export(filepath=output_mesh_path) + +def process_meshes(input_path, output_path): + """ + Processes each mesh file in the input directory and saves the cleaned meshes in the output directory. + + Args: + input_path (str): Directory containing the input meshes or a single mesh file. + output_path (str): Directory where the cleaned meshes will be saved. + """ + if os.path.isdir(input_path): + for filename in sorted(os.listdir(input_path)): + full_input_path = os.path.join(input_path, filename) + full_output_path = os.path.join(output_path, filename) + process(full_input_path, full_output_path) + else: + process(input_path, output_path) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Processes mesh files.") + parser.add_argument("-ip", "--input_path", type=str, required=True, help="Input path for the mesh file or directory.") + parser.add_argument("-op", "--output_path", type=str, required=True, help="Output path for the cleaned mesh file or directory.") + args = parser.parse_args() + process_meshes(args.input_path, args.output_path) diff --git a/tools/optimize_model/requirements.txt b/tools/optimize_model/requirements.txt new file mode 100644 index 0000000..ec8d4be --- /dev/null +++ b/tools/optimize_model/requirements.txt @@ -0,0 +1,5 @@ +open3d +numpy +sklearn +argparse +bpy diff --git a/tools/optimize_model/run.py b/tools/optimize_model/run.py new file mode 100644 index 0000000..c0735a0 --- /dev/null +++ b/tools/optimize_model/run.py @@ -0,0 +1,78 @@ + +import os,sys,time,shlex,subprocess,shutil,requests,json +import platform +if platform.system() == 'Windows': + sys.path.append('e:\\libs\\') + #sys.path.append('libs') +else: + sys.path.append('/data/deploy/make3d/make2/libs/') +import libs,config +def main(pid = 0): + print(f'开始时间-{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}') + if pid == 0: + #获取要处理PID的url + url = "https://repair.api.suwa3d.com/api/modelRepairOrder/getRepairOrderListByPriority" + #发起请求 + res = requests.get(url) + print('res:', res.text) + if res.status_code != 200: + print('获取失败,程序退出') + return + res = json.loads(res.text) + pid = 0 + try: + pid = res['data']['pid'] + except Exception as e: + return 0 + + if pid == 0: + return 0 + + #调用oss下载功能,下载对应的文件 + pathStr = os.path.join(config.workdir,str(pid)+"_ai") + libs.down_obj_from_oss_auto(str(pid),"auto",pathStr) + #检查是否存在产生的对应的文件 + if not os.path.exists(os.path.join(pathStr,f'{pid}.obj')): + print(f"找不到{pid}obj文件") + return + + if not os.path.exists(os.path.join(pathStr,f'{pid}.mtl')): + print(f"找不到{pid}mtl文件") + return + #下载完成后调用 + os.system(f"python optimize_model.py -ip {pathStr}/{pid}.obj -op {pathStr}/{pid}_ai.obj") + #检查是否存在产生的对应的文件 + if not os.path.exists(os.path.join(pathStr,f'{pid}_ai.obj')): + print(f"找不到{pid}ai_obj文件") + return + + if not os.path.exists(os.path.join(pathStr,f'{pid}_ai.mtl')): + print(f"找不到{pid}ai_mtl文件") + return + + #处理完成完后上传到oss指定目录 + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/ai/fix_mesh/{pid}_ai.obj', os.path.join(pathStr,f'{pid}_ai.obj')) + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/ai/fix_mesh/{pid}_ai.mtl', os.path.join(pathStr,f'{pid}_ai.mtl')) + config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/ai/fix_mesh/{pid}.jpg', os.path.join(pathStr,f'{pid}.jpg')) + #更新处理结果 + url = "https://repair.api.suwa3d.com/api/modelRepairOrder/updateShootOrderBuildModelType?pid="+str(pid) + #发起请求 + requests.get(url) + #移除文件夹 + shutil.rmtree(pathStr) + print(f'结束时间-{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}') + + + +if __name__ == '__main__': + + if len(sys.argv) == 2: + pid = sys.argv[1] + main(pid) + print(f"{pid}-处理结束") + else: + while True: + res = main() + if res == 0: + print("休眠60s") + time.sleep(60) \ No newline at end of file diff --git a/tools/optimize_model/test.py b/tools/optimize_model/test.py new file mode 100644 index 0000000..3a87e08 --- /dev/null +++ b/tools/optimize_model/test.py @@ -0,0 +1,22 @@ +import paramiko + +try: + # 创建SSH对象 + ssh = paramiko.SSHClient() + # 允许连接不在known_hosts文件中的主机 + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + print('start connect') + # 连接服务器 + ssh.connect('connect.bjc1.seetacloud.com', username='root', password='QoQA8q3Ds2VB', port=33733) + print('connect success') + + # 使用nohup执行命令,并将输出重定向到文件 + command = 'nohup seg_python /data/code/optimize_model_xj/optimize_model.py -pid 189813 > output.log 2>&1 &' + stdin, stdout, stderr = ssh.exec_command(command) + + # 打印命令是否启动成功 + print("Command executed") +finally: + # 确保连接被关闭 + if ssh: + ssh.close() diff --git a/tools/push_cmd.py b/tools/push_cmd.py index c8f9223..651901d 100644 --- a/tools/push_cmd.py +++ b/tools/push_cmd.py @@ -1,10 +1,10 @@ -import redis, os, sys,requests,platform +import redis, os, sys,requests,platform,json if platform.system() == 'Windows': - sys.path.append('e:\\libs\\') + sys.path.append('E:\\make2\\libs\\') #sys.path.append('libs') else: sys.path.append('/data/deploy/make3d/make2/libs/') -import config, libs,libs_db +import config, libs,libs_db,libs_db_task_rc def main(cmd, order_id): if cmd == 'print': #order_id @@ -19,17 +19,45 @@ def main(cmd, order_id): key = 'model:foot' elif cmd == 'rebuild': #pid key = 'model:rebuild' + elif cmd == 'gs': + key = 'ai:ai_build' if order_id == 'view': - for i in r.lrange(key, 0, -1): - print(i) - print(f'当前{key}队列长度:{r.llen(key)}') + if cmd == "rc_model_build": + nums = libs_db_task_rc.get_task_rc_count() + print(f'当前{cmd}队列长度:{nums}') + notify(f'当前{cmd}队列长度:{nums}') + else: + print(f'当前{cmd}队列长度:{r.llen(key)}') + notify(f'当前{cmd}队列长度:{r.llen(key)}') + + + for i in r.lrange(key, 0, -1): + print(i) + print(f'当前{key}队列长度:{r.llen(key)}') else: order_ids = order_id.split(',') for order_id in order_ids: r.lpush(key, order_id) print(f'已推送{order_id}到{key}, 当前队列长度:{r.llen(key)}') + +def notify(content): + + if content == "": + return "content 不能为空" + + for user_agent_id in config.notify_user_Ids: + data = { + 'userId': user_agent_id, + 'message': content, + } + headers = {'Content-Type': 'application/json'} + message_send_url = "https://mp.api.suwa3d.com/api/qyNotify/sendMessage?userId="+user_agent_id+"&message="+content + response = requests.post(message_send_url, data=json.dumps(data), headers=headers) + + + def cmd(cmdName,pid): if pid == "view": key = "" @@ -43,10 +71,18 @@ def cmd(cmdName,pid): key = 'model:make10' elif cmdName == 'foot': # print_id key = 'model:foot' - for i in r.lrange(key, 0, -1): - print(i) - print(f'当前{key}队列长度:{r.llen(key)}') - exit() + + if pid == 'view': + # for i in r.lrange(key, 0, -1): + # print(i) + print(f'当前{cmd}队列长度:{r.llen(key)}') + notify(f'当前{cmd}队列长度:{r.llen(key)}') + exit() + else: + for i in r.lrange(key, 0, -1): + print(i) + print(f'当前{key}队列长度:{r.llen(key)}') + exit() res = requests.get(f'https://mp.api.suwa3d.com/api/infoQuery/infoByPid?pid={pid}&cmd={cmdName}') #获取res 的数据 @@ -135,13 +171,24 @@ def listDataToStr(data,cmdName): if __name__ == '__main__': + r = config.redis_remote if len(sys.argv) == 3: cmdName = sys.argv[1] pid = sys.argv[2] + + elif len(sys.argv) == 2: + cmd = sys.argv[1] + order_id = 'view' + if cmd == "auto_view": + arrCmd = ["print","foot","gs","rc_model_build"] + for cmdV in arrCmd: + main(cmdV, order_id) + + exit(1) else: print('用法:python push_cmd.py ') exit(1) - r = config.redis_remote + #pid 可能是多个的,用逗号分隔,查询的时候接口应该也要支持多个的 if cmdName == 'make3d' or cmdName == 'make3d10' or cmdName == 'rebuild': diff --git a/建模碎片步骤.txt b/建模碎片步骤.txt index dfeb31b..bfb847d 100644 --- a/建模碎片步骤.txt +++ b/建模碎片步骤.txt @@ -1,3 +1,42 @@ #建模完成,需要减面 smooth 封闭洞 清理模型 贴图 -%bin% -delegateTo %pid% -simplify 3000000 -smooth -closeHoles -cleanModel -calculateTexture -save "D:\123446\123446.rcproj" -exportSelectedModel "D:\123446\output\123446.obj" "d:\make2\config\ModelExportParams.xml" -quit \ No newline at end of file +%bin% -delegateTo 161082 -simplify 2000000 -smooth -closeHoles -cleanModel -calculateTexture -save "D:\161082\161082.rcproj" -exportSelectedModel "D:\161082\output\161082.obj" "d:\make2\config\ModelExportParams.xml" + + + +%bin% -delegateTo 161082 -defineDistance 36h11:007 36h11:008 0.21 -update -align -align -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:003 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" + + +%bin% -delegateTo 156600 -defineDistance 36h11:001 36h11:002 1 -update -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:004 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" + + +%bin% -delegateTo 156600 -defineDistance 36h11:007 36h11:008 0.21 -update -align -align -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:004 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" -exportReconstructionRegion "D:\156600\156600.rcbox" -selectImage "D:\156600\photo2/*" -enableTexturingAndColoring true -save "D:\156600\156600.rcproj" + +%bin% -delegateTo 156600 -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:003 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" -exportControlPointsMeasurements "D:\156600\156600.controlPoints.csv" "D:\make2\config\exportControlPoints.config.xml" -exportReconstructionRegion "D:\156600\156600.rcbox" -selectImage "D:\156600\photo2\*" -enableTexturingAndColoring true -save "D:\156600\156600.rcproj" + + + -defineDistance 36h11:007 36h11:008 0.21 -update -align -align -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:003 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" -exportReconstructionRegion "D:\140241\140241.rcbox" -selectImage "D:\140241\photo2/*" -enableTexturingAndColoring true -save "D:\140241\140241.rcproj" + +-defineDistance 36h11:007 36h11:008 0.21 -align -align -update -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:003 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" -exportControlPointsMeasurements "D:\147024\147024.controlPoints.csv" "D:\make2\config\exportControlPoints.config.xml" -exportReconstructionRegion "D:\147024\147024.rcbox" -selectImage "D:\147024\photo2\*" -enableTexturingAndColoring true -save "D:\147024\147024.rcproj" + + +%bin% -delegateTo 155007 -defineDistance 36h11:007 36h11:008 0.21 -update -align -align -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:004 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" -exportReconstructionRegion "D:\155007\155007.rcbox" -save "D:\155007\155007.rcproj" + + + + +%bin% -delegateTo 153982 -defineDistance 36h11:007 36h11:008 0.21 -update -align -align -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:004 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" -exportReconstructionRegion "D:\153982\153982.rcbox" -selectImage "D:\153982\photo2/*" -enableTexturingAndColoring true -save "D:\153982\153982.rcproj" + + +%bin% -delegateTo 155686 -defineDistance 36h11:007 36h11:008 0.21 -align -align -update -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:004 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" -exportControlPointsMeasurements "D:\155686\155686.controlPoints.csv" "D:\make2\config\exportControlPoints.config.xml" -exportReconstructionRegion "D:\155686\155686.rcbox" -selectImage "D:\155686\photo2\*" -enableTexturingAndColoring true -save "D:\155686\155686.rcproj" + +%bin% -delegateTo 155686 -defineDistance 36h11:007 36h11:008 0.21 -defineDistance 36h11:002 36h11:004 1 -defineDistance 36h11:004 36h11:003 1 -defineDistance 36h11:003 36h11:001 1 -align -align -update -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:003 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" -exportControlPointsMeasurements "D:\155686\155686.controlPoints.csv" "D:\make2\config\exportControlPoints.config.xml" -exportReconstructionRegion "D:\155686\155686.rcbox" -selectImage "D:\155686\photo2\*" -enableTexturingAndColoring true -save "D:\155686\155686.rcproj" + + + +%bin% -delegateTo 158508 -defineDistance 36h11:007 36h11:008 0.21 -align -align -update -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:004 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" -exportControlPointsMeasurements "D:\158508\158508.controlPoints.csv" "D:\make2\config\exportControlPoints.config.xml" -exportReconstructionRegion "D:\158508\158508.rcbox" -selectImage "D:\158508\photo2\*" -enableTexturingAndColoring true -save "D:\158508\158508.rcproj" + + + + +%bin% -delegateTo 161086 -defineDistance 36h11:007 36h11:008 0.21 -align -align -update -setReconstructionRegionOnCPs 36h11:001 36h11:002 36h11:004 2.1 -moveReconstructionRegion 0 0 -2.1 -rotateReconstructionRegion 180 0 180 -setGroundPlaneFromReconstructionRegion -scaleReconstructionRegion 1.8 1.6 2.1 absolute center -moveReconstructionRegion 0 0 0.0025 -exportXMP "D:\make2\config\exportXMP.config.xml" -exportControlPointsMeasurements "D:\161086\161086.controlPoints.csv" "D:\make2\config\exportControlPoints.config.xml" -exportReconstructionRegion "D:\161086\161086.rcbox" -selectImage "D:\161086\photo2\*" -enableTexturingAndColoring true -save "D:\161086\161086.rcproj" \ No newline at end of file