first commit
This commit is contained in:
commit
677a836569
29 files changed
+1785
No files matched your search
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,40 @@
|
|||||||
|
<Configuration>
|
||||||
|
<entry key="ModelExportFormatVersion" value="0"/>
|
||||||
|
<entry key="MvsMeshExportCamerasAsModelPart" value="false"/>
|
||||||
|
<entry key="MvsMeshExportTexturingAllowed" value="-1"/>
|
||||||
|
<entry key="MvsExportIsModelCoordinates" value="0"/>
|
||||||
|
<entry key="MvsExportIsGeoreferenced" value="0x1"/>
|
||||||
|
<entry key="MvsExportScaleZ" value="1.0"/>
|
||||||
|
<entry key="MvsMeshExportTileType" value="0"/>
|
||||||
|
<entry key="MvsMeshExportNormals" value="false"/>
|
||||||
|
<entry key="MvsExportScaleY" value="1.0"/>
|
||||||
|
<entry key="MvsMeshExportTexAlpha" value="false"/>
|
||||||
|
<entry key="MvsExportScaleX" value="1.0"/>
|
||||||
|
<entry key="MvsMeshExportTexImgFormat_Color8_0" value="jpg"/>
|
||||||
|
<entry key="MvsExportcoordinatesystemtype" value="0"/>
|
||||||
|
<entry key="MvsMeshExportTexPixFormat_Color8_0" value="32bppBGRA"/>
|
||||||
|
<entry key="MvsMeshExportNormalsAllowed" value="-1"/>
|
||||||
|
<entry key="MvsMeshExportNumberFormatAllowed" value="-1"/>
|
||||||
|
<entry key="MvsExportMoveZ" value="0.0"/>
|
||||||
|
<entry key="MvsExportMoveX" value="0.0"/>
|
||||||
|
<entry key="MvsExportMoveY" value="0.0"/>
|
||||||
|
<entry key="MvsExportNormalRange" value="ZeroToOne"/>
|
||||||
|
<entry key="MvsMeshExportInfoFile" value="true"/>
|
||||||
|
<entry key="MvsMeshExportByParts" value="0"/>
|
||||||
|
<entry key="MvsMeshExportClassificationAllowed" value="0"/>
|
||||||
|
<entry key="MvsMeshExportNumberFormat" value="5"/>
|
||||||
|
<entry key="MvsExportRotationY" value="0.0"/>
|
||||||
|
<entry key="MvsExportNormalFlipZ" value="false"/>
|
||||||
|
<entry key="MvsExportRotationX" value="0.0"/>
|
||||||
|
<entry key="MvsExportNormalFlipY" value="false"/>
|
||||||
|
<entry key="MvsMeshExportCamerasAllowed" value="0"/>
|
||||||
|
<entry key="MvsMeshExportColors" value="true"/>
|
||||||
|
<entry key="MvsExportNormalSpace" value="Mikktspace"/>
|
||||||
|
<entry key="MvsExportNormalFlipX" value="false"/>
|
||||||
|
<entry key="MvsExportTransformationPreset" value="[[Default]]"/>
|
||||||
|
<entry key="MvsExportRotationZ" value="0.0"/>
|
||||||
|
<entry key="MvsMeshExportFileTypeSelectionDisplay" value="0"/>
|
||||||
|
<entry key="MvsMeshExportTexOneFile" value="1"/>
|
||||||
|
<entry key="MvsMeshExportTexturing" value="-1"/>
|
||||||
|
<entry key="MvsMeshExportEmbeddTxrsAllowed" value="0"/>
|
||||||
|
</Configuration>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<ModelExport exportBinary="1" exportInfoFile="1" exportVertices="1" exportVertexColors="2"
|
||||||
|
exportVertexNormals="0" exportTriangles="1" exportTexturing="1" meshColor="4294967295"
|
||||||
|
tileType="0" exportTextureAlpha="0" exportToOneTexture="1" oneTextureMaxSide="8192"
|
||||||
|
oneTextureUsePow2TexSide="1" exportCoordinateSystemType="0" settingsAnchor="0 0 0"
|
||||||
|
settingsScalex="1" settingsScaley="1" settingsScalez="1" texturesFileType="jpg"
|
||||||
|
formatAndVersionUID="obj 000 " exportModelByParts="0" exportRandomPartColor="0"
|
||||||
|
exportCameras="0" exportCamerasAsModelPart="0" numberAsciiFormatting="%.16e">
|
||||||
|
<Header magic="5786949" version="1"/>
|
||||||
|
</ModelExport>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<Configuration id="{8D21413B-0848-49A9-BF6E-8EBCCA356BC7}">
|
||||||
|
<entry key="minMarkerMeasurements" value="0x1"/>
|
||||||
|
<entry key="generateMarkersPaperSize" value="0"/>
|
||||||
|
<entry key="generateMarkersMarkersPerPage" value="0x4"/>
|
||||||
|
<entry key="generateMarkersCount" value="0x4"/>
|
||||||
|
<entry key="markerType" value="AprilTag36h11"/>
|
||||||
|
</Configuration>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
D1 36h11:001 36h11:002 1
|
||||||
|
D2 36h11:002 36h11:004 1
|
||||||
|
D3 36h11:004 36h11:003 1
|
||||||
|
D4 36h11:003 36h11:001 1
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<Configuration id="{E80BAEB3-7A87-4889-B150-0CF6486D7089}">
|
||||||
|
<entry key="icpsAcc" value="4.0"/>
|
||||||
|
<entry key="csvCPMSep" value="2"/>
|
||||||
|
<entry key="icpsTmode" value="1"/>
|
||||||
|
<entry key="csvCPMIgn" value="false"/>
|
||||||
|
</Configuration>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<Configuration id="{EC40D990-B2AF-42A4-9637-1208A0FD1322}">
|
||||||
|
<entry key="xmpMerge" value="true"/>
|
||||||
|
<entry key="xmpExGps" value="true"/>
|
||||||
|
<entry key="xmpFlags" value="true"/>
|
||||||
|
<entry key="xmpCalibGroups" value="true"/>
|
||||||
|
<entry key="xmpCamera" value="1"/>
|
||||||
|
<entry key="xmpRig" value="true"/>
|
||||||
|
</Configuration>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
101668,102013,101997,102049,102097,102123,102139,102142,102153,102058
|
||||||
+13
@@ -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"
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,91 @@
|
|||||||
|
import oss2, redis, platform
|
||||||
|
|
||||||
|
baidu_api = {
|
||||||
|
'face': {
|
||||||
|
'app_id': '26878271',
|
||||||
|
'api_key': '01CQzxLCpGrLjGe2ClKTC8hx',
|
||||||
|
'secret_key': '56WHgdlEvGG4iA9KAEn51naiXy31ybKa',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ali_oss = {
|
||||||
|
'access_key_id': 'LTAI5tSReWm8hz7dSYxxth8f',
|
||||||
|
'access_key_secret': '8ywTDF9upPAtvgXtLKALY2iMYHIxdS',
|
||||||
|
'facebody_endpoint': 'facebody.cn-shanghai.aliyuncs.com',
|
||||||
|
'endpoint': 'oss-cn-shanghai.aliyuncs.com',
|
||||||
|
'bucket_name': 'suwa3d-securedata',
|
||||||
|
}
|
||||||
|
oss_bucket = oss2.Bucket(oss2.Auth(ali_oss['access_key_id'], ali_oss['access_key_secret']), ali_oss['endpoint'], ali_oss['bucket_name'])
|
||||||
|
|
||||||
|
redis_remote = redis.Redis(host='106.14.158.208', password='kcV2000', port=6379, db=6)
|
||||||
|
redis_local = redis.Redis(host='172.16.20.13', password='ph2008', port=6379, db=0)
|
||||||
|
mysql_local = {
|
||||||
|
"host": "172.16.20.13",
|
||||||
|
"port": 3306,
|
||||||
|
"user": "pi",
|
||||||
|
"password": "ph2008",
|
||||||
|
"db": "suwa3d",
|
||||||
|
"charset": "utf8mb4"
|
||||||
|
}
|
||||||
|
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
workdir = 'D:\\'
|
||||||
|
sharedir = 'E:\\'
|
||||||
|
rcbin = '"C:\\Program Files\\Capturing Reality\\RealityCapture\\RealityCapture.exe"'
|
||||||
|
else:
|
||||||
|
workdir = '/data/datasets/'
|
||||||
|
|
||||||
|
urls = {
|
||||||
|
'update_status_modeling_url': 'https://mp.api.suwa3d.com/api/customerP3dLog/toModeling',
|
||||||
|
'update_status_modelsuccess_url': 'https://repair.api.suwa3d.com/api/modelRepairOrder/toModelMakeSucceed',
|
||||||
|
'update_status_modelfailed_url': 'https://mp.api.suwa3d.com/api/customerP3dLog/toModelMakeFailed',
|
||||||
|
'get_psid_url': 'https://mp.api.suwa3d.com/api/customerP3dLog/photoStudio',
|
||||||
|
'get_printinfo_url': 'https://mp.api.suwa3d.com/api/customerP3dLog/printInfo',
|
||||||
|
'update_status_printstatus_url': 'https://mp.api.suwa3d.com/api/customerP3dLog/updateBuildPrintModelStatus',
|
||||||
|
'get_ps_adjust_photo_para_url': 'https://mp.api.suwa3d.com/api/equipment/configForColor',
|
||||||
|
'get_ps_type_url' : 'https://mp.api.suwa3d.com/api/takephotoOrder/photoStudioInfo',
|
||||||
|
'get_printsize_url' : 'https://mp.api.suwa3d.com/api/printOrder/info',
|
||||||
|
'upload_model_info_url' : 'https://mp.api.suwa3d.com/api/physical/add',
|
||||||
|
}
|
||||||
|
|
||||||
|
r = {
|
||||||
|
"setTextureTrue" : "-selectAllImages -enableTexturingAndColoring true",
|
||||||
|
"setTextureFalse" : "-selectAllImages -enableTexturingAndColoring false",
|
||||||
|
}
|
||||||
|
|
||||||
|
r1 = {
|
||||||
|
"init" : "-disableOnlineCommunication -set \"sfmEnableCameraPrior=False\" -set \"sfmMaxFeaturesPerMpx=20000\" -set \"sfmMaxFeaturesPerImage=200000\" -set \"sfmImagesOverlap=High\" -set \"sfmMaxFeatureReprojectionError=1\"",
|
||||||
|
}
|
||||||
|
|
||||||
|
r2 = {
|
||||||
|
"init" : "-disableOnlineCommunication -setProjectCoordinateSystem Local:1 -setOutputCoordinateSystem epsg:4326 -set \"sfmEnableCameraPrior=False\" -set \"sfmMaxFeaturesPerMpx=20000\" -set \"sfmMaxFeaturesPerImage=200000\" -set \"sfmImagesOverlap=High\" -set \"sfmMaxFeatureReprojectionError=1\"",
|
||||||
|
"setRegion" : "-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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 影棚地贴版本定义
|
||||||
|
floor_sticker_distances = {
|
||||||
|
# 影棚地贴版本1:老圆形影棚,二维码排序1、2、3、4、1,间距1米
|
||||||
|
"v1" : "36h11:001 36h11:002 1;36h11:002 36h11:003 1;36h11:003 36h11:004 1;36h11:004 36h11:001 1",
|
||||||
|
# 影棚地贴版本2:新方形影棚,二维码排序1、2、4、3、1,间距1米 default
|
||||||
|
"v2" : "36h11:001 36h11:002 1;36h11:002 36h11:004 1;36h11:004 36h11:003 1;36h11:003 36h11:001 1",
|
||||||
|
# 影棚地贴版本3:新圆形影棚,二维码排序1、2、4、3、1,间距1.5米
|
||||||
|
"v3" : "36h11:001 36h11:002 1.5;36h11:002 36h11:004 1.5;36h11:004 36h11:003 1.5;36h11:003 36h11:001 1.5",
|
||||||
|
# 影棚地贴版本4:新方形影棚,二维码排序5、6,间距0.21米
|
||||||
|
"v4" : "36h11:005 36h11:006 0.21",
|
||||||
|
# 影棚地贴版本5:新方形影棚,二维码排序7、8,间距0.21米
|
||||||
|
"v5" : "36h11:007 36h11:008 0.21"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 影棚与地贴版本配置关系
|
||||||
|
ps_floor_sticker = {
|
||||||
|
"default" : floor_sticker_distances['v2'],
|
||||||
|
"1" : floor_sticker_distances['v5'],
|
||||||
|
"13" : floor_sticker_distances['v1'],
|
||||||
|
"17" : floor_sticker_distances['v1'],
|
||||||
|
"18" : floor_sticker_distances['v1'],
|
||||||
|
"29" : floor_sticker_distances['v1'],
|
||||||
|
"85" : floor_sticker_distances['v4'],
|
||||||
|
}
|
||||||
|
|
||||||
|
# 需要加入新建模系统的影棚
|
||||||
|
new_make_psids = ['1', '29', '44', '54', '55', '63', '65', '77', '79', '80', '85', '86']
|
||||||
+272
@@ -0,0 +1,272 @@
|
|||||||
|
import os, time, json, requests, shutil, oss2, psutil
|
||||||
|
from tqdm import tqdm
|
||||||
|
from PIL import Image, ImageEnhance
|
||||||
|
import config
|
||||||
|
|
||||||
|
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)
|
||||||
|
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):
|
||||||
|
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]
|
||||||
|
# print('正在下载:', file.key)
|
||||||
|
if filename.endswith('_1.jpg'):
|
||||||
|
localfile = os.path.join(path, 'photo1', filename)
|
||||||
|
else:
|
||||||
|
localfile = os.path.join(path, 'photo2', filename)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 图片下载完成, 共费时{diff_time(start_time)}')
|
||||||
|
|
||||||
|
def get_defineDistances(distances):
|
||||||
|
res = ''
|
||||||
|
distances = distances.split(';')
|
||||||
|
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)}秒'
|
||||||
|
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# mysql数据库常用任务函数封装
|
||||||
|
import pymysql, socket, time
|
||||||
|
import config
|
||||||
|
|
||||||
|
# 获取新的任务
|
||||||
|
def get_task(task_type):
|
||||||
|
try:
|
||||||
|
with pymysql.connect(
|
||||||
|
host=config.mysql_local['host'],
|
||||||
|
port=config.mysql_local['port'],
|
||||||
|
user=config.mysql_local['user'],
|
||||||
|
password=config.mysql_local['password'],
|
||||||
|
db=config.mysql_local['db'],
|
||||||
|
charset=config.mysql_local['charset'],) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
sql = f'select task_key from tasks where task_type = "{task_type}" and status = 0 order by id asc limit 1'
|
||||||
|
# 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({task_type})异常: {str(e)}")
|
||||||
|
return ''
|
||||||
|
|
||||||
|
# 新增新的任务
|
||||||
|
def add_task(data):
|
||||||
|
try:
|
||||||
|
with pymysql.connect(
|
||||||
|
host=config.mysql_local['host'],
|
||||||
|
port=config.mysql_local['port'],
|
||||||
|
user=config.mysql_local['user'],
|
||||||
|
password=config.mysql_local['password'],
|
||||||
|
db=config.mysql_local['db'],
|
||||||
|
charset=config.mysql_local['charset'],) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
sql = f'insert into tasks (task_type, task_key) values ("{data["task_type"]}", "{data["task_key"]}")'
|
||||||
|
# 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 start_task(data):
|
||||||
|
try:
|
||||||
|
with pymysql.connect(
|
||||||
|
host=config.mysql_local['host'],
|
||||||
|
port=config.mysql_local['port'],
|
||||||
|
user=config.mysql_local['user'],
|
||||||
|
password=config.mysql_local['password'],
|
||||||
|
db=config.mysql_local['db'],
|
||||||
|
charset=config.mysql_local['charset'],) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
sql = f'update tasks set status = 1, hostname = "{hostname}", started_at = now(), updated_at = now() where task_type = "{data["task_type"]}" and task_key = "{data["task_key"]}"'
|
||||||
|
# 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())} 执行start_task({data})异常: {str(e)}")
|
||||||
|
|
||||||
|
# 完成任务
|
||||||
|
def finish_task(data):
|
||||||
|
try:
|
||||||
|
with pymysql.connect(
|
||||||
|
host=config.mysql_local['host'],
|
||||||
|
port=config.mysql_local['port'],
|
||||||
|
user=config.mysql_local['user'],
|
||||||
|
password=config.mysql_local['password'],
|
||||||
|
db=config.mysql_local['db'],
|
||||||
|
charset=config.mysql_local['charset'],) as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
sql = f'update tasks set status = 2, hostname = "{hostname}", finished_at = now(), updated_at = now() where task_type = "{data["task_type"]}" and task_key = "{data["task_key"]}"'
|
||||||
|
# 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())} 执行finish_task({data})异常: {str(e)}")
|
||||||
+208
@@ -0,0 +1,208 @@
|
|||||||
|
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\\')
|
||||||
|
else:
|
||||||
|
sys.path.append('/data/deploy/make3d/make2/libs/')
|
||||||
|
import config, libs, libs_db
|
||||||
|
|
||||||
|
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 = ' '
|
||||||
|
if os.path.exists(os.path.join(config.workdir, pid, 'photo3')):
|
||||||
|
add_photo3 = ' -addFolder "' + os.path.join(config.workdir, pid, 'photo3') + '" '
|
||||||
|
|
||||||
|
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(config.ps_floor_sticker.get(psid, config.ps_floor_sticker["default"]))} -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")}" \
|
||||||
|
-save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -quit'
|
||||||
|
print(cmd)
|
||||||
|
cmd = shlex.split(cmd)
|
||||||
|
res = subprocess.run(cmd)
|
||||||
|
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')
|
||||||
|
|
||||||
|
def cal_reconstruction_region(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]
|
||||||
|
add_photo3 = ' '
|
||||||
|
if os.path.exists(os.path.join(config.workdir, pid, 'photo3')):
|
||||||
|
add_photo3 = ' -addFolder "' + os.path.join(config.workdir, pid, 'photo3') + '" '
|
||||||
|
cmd = f'{config.rcbin} {config.r2["init"]} -setInstanceName {pid} \
|
||||||
|
-load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" \
|
||||||
|
-addFolder "{os.path.join(config.workdir, pid, "photo2")}" -align {add_photo3} \
|
||||||
|
-detectMarkers "D:\\make2\\config\\detectMarkers.config.xml" {libs.get_defineDistances(config.ps_floor_sticker.get(psid, config.ps_floor_sticker["default"]))} -align -align \
|
||||||
|
-update {config.r2["setRegion"]} \
|
||||||
|
-exportControlPointsMeasurements "{os.path.join(config.workdir, pid, f"{pid}.controlPoints.csv")}" "D:\\make2\\config\\exportControlPoints.config.xml" \
|
||||||
|
-selectAllImages -exportXMP "D:\\make2\\config\\exportXMP.config.xml" \
|
||||||
|
-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)
|
||||||
|
|
||||||
|
fix_region()
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 重建区域计算完成')
|
||||||
|
|
||||||
|
def step1(pid, experience=False, makeloop=True):
|
||||||
|
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} 开始下载图片...')
|
||||||
|
if experience:
|
||||||
|
libs.down_from_oss(config.oss_bucket, config.workdir, pid, per=50)
|
||||||
|
else:
|
||||||
|
libs.down_from_oss(config.oss_bucket, config.workdir, pid)
|
||||||
|
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)}')
|
||||||
|
|
||||||
|
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)}')
|
||||||
|
|
||||||
|
# TODO: 更新本地step1任务状态,加入step2任务队列
|
||||||
|
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任务完成,移动到共享目录')
|
||||||
|
|
||||||
|
def main(pid, experience=False, makeloop=True):
|
||||||
|
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 = True
|
||||||
|
step1(pid, experience, makeloop)
|
||||||
|
else:
|
||||||
|
step1(pid, experience, makeloop)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# 取云端redis任务,完成第一步的数据预处理后,将数据放入共享存储目录,将第二步任务塞入本地mysql队列
|
||||||
|
# 默认循环值守,可传参数运行单一任务,以方便调试
|
||||||
|
pid = '0'
|
||||||
|
if len(sys.argv) == 2:
|
||||||
|
pids = sys.argv[1].split(',')
|
||||||
|
for pid in pids:
|
||||||
|
main(pid, experience=False, makeloop=False)
|
||||||
|
exit()
|
||||||
|
if len(sys.argv) == 3:
|
||||||
|
experience = False
|
||||||
|
if sys.argv[2] == '1':
|
||||||
|
print('演示测试...')
|
||||||
|
experience = True
|
||||||
|
pids = sys.argv[1].split(',')
|
||||||
|
for pid in pids:
|
||||||
|
main(pid, experience=experience, makeloop=False)
|
||||||
|
exit()
|
||||||
|
main(pid, experience=False, makeloop=True)
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import os, sys, time, shutil, subprocess, shlex
|
||||||
|
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 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):
|
||||||
|
simplify_value = 1000000 * libs.getHeadCount(pid)
|
||||||
|
add_photo3 = ' '
|
||||||
|
if os.path.exists(os.path.join(config.workdir, pid, 'photo3')):
|
||||||
|
add_photo3 = ' -addFolder "' + os.path.join(config.workdir, pid, 'photo3') + '" -align -align '
|
||||||
|
|
||||||
|
if get_rcver() == 1: # old version
|
||||||
|
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 -save "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}"'
|
||||||
|
print(cmd)
|
||||||
|
cmd = shlex.split(cmd)
|
||||||
|
res = subprocess.run(cmd)
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 定位点导入完成')
|
||||||
|
|
||||||
|
# defind_distance
|
||||||
|
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 定义定位点距离完成')
|
||||||
|
|
||||||
|
cmd = f'{config.rcbin} {config.r1["init"]} -load "{os.path.join(config.workdir, pid, f"{pid}.rcproj")}" -update \
|
||||||
|
-setReconstructionRegion "{os.path.join(config.workdir, pid, f"{pid}.rcbox")}" \
|
||||||
|
-mvs -modelSelectMaximalConnectedComponent -modelInvertSelection -modelRemoveSelectedTriangles -closeHoles -clean -simplify {simplify_value} -smooth -unwrap -calculateTexture -renameModel {pid} -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 libs.aliyun_face(pid) and libs.get_ps_type(pid) == 1:
|
||||||
|
calulate_type = 'calculateHighModel'
|
||||||
|
else:
|
||||||
|
calulate_type = 'calculateNormalModel'
|
||||||
|
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'
|
||||||
|
print(cmd)
|
||||||
|
cmd = shlex.split(cmd)
|
||||||
|
res = subprocess.run(cmd)
|
||||||
|
|
||||||
|
def step2(pid):
|
||||||
|
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
|
||||||
|
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任务队列
|
||||||
|
|
||||||
|
os.system(f'python d:\\make2\\main_step3.py {pid}')
|
||||||
|
|
||||||
|
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)
|
||||||
+229
@@ -0,0 +1,229 @@
|
|||||||
|
import os, sys, time, bpy, math, requests, bmesh, json, shutil
|
||||||
|
from PIL import Image
|
||||||
|
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 bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False):
|
||||||
|
"""Returns a transformed, triangulated copy of the mesh"""
|
||||||
|
assert obj.type == 'MESH'
|
||||||
|
if apply_modifiers and obj.modifiers:
|
||||||
|
import bpy
|
||||||
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
|
obj_eval = obj.evaluated_get(depsgraph)
|
||||||
|
me = obj_eval.to_mesh()
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(me)
|
||||||
|
obj_eval.to_mesh_clear()
|
||||||
|
else:
|
||||||
|
me = obj.data
|
||||||
|
if obj.mode == 'EDIT':
|
||||||
|
bm_orig = bmesh.from_edit_mesh(me)
|
||||||
|
bm = bm_orig.copy()
|
||||||
|
else:
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(me)
|
||||||
|
if transform:
|
||||||
|
matrix = obj.matrix_world.copy()
|
||||||
|
if not matrix.is_identity:
|
||||||
|
bm.transform(matrix)
|
||||||
|
matrix.translation.zero()
|
||||||
|
if not matrix.is_identity:
|
||||||
|
bm.normal_update()
|
||||||
|
if triangulate:
|
||||||
|
bmesh.ops.triangulate(bm, faces=bm.faces)
|
||||||
|
return bm
|
||||||
|
|
||||||
|
def find_pid_objname(pid):
|
||||||
|
for obj in bpy.data.objects:
|
||||||
|
if obj.name.startswith(str(pid)):
|
||||||
|
return obj.name
|
||||||
|
|
||||||
|
def reload_obj(pid):
|
||||||
|
obj_filename = os.path.join(config.workdir, pid, 'output', f'{pid}.obj')
|
||||||
|
bpy.ops.wm.read_homefile()
|
||||||
|
bpy.ops.object.delete(use_global=False, confirm=False)
|
||||||
|
bpy.ops.import_scene.obj(filepath=obj_filename)
|
||||||
|
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)
|
||||||
|
|
||||||
|
def base_fix(pid):
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始模型基础校正...')
|
||||||
|
start_time = time.time()
|
||||||
|
# 统一文件名规则
|
||||||
|
def fix_filename(pid):
|
||||||
|
if os.path.exists(os.path.join(config.workdir, pid, 'output', f'{pid}.jpg')):
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 已经是最新文件名规则,无需处理')
|
||||||
|
return
|
||||||
|
elif os.path.exists(os.path.join(config.workdir, pid, 'output', f'{pid}_u0_v0_diffuse.jpg')):
|
||||||
|
texture_filename_end = '_u0_v0_diffuse.jpg'
|
||||||
|
elif os.path.exists(os.path.join(config.workdir, pid, 'output', f'{pid}_u1_v1.jpg')):
|
||||||
|
texture_filename_end = '_u1_v1.jpg'
|
||||||
|
os.rename(os.path.join(config.workdir, pid, 'output', f'{pid}{texture_filename_end}'), os.path.join(config.workdir, pid, 'output', f'{pid}.jpg'))
|
||||||
|
with open(os.path.join(config.workdir, pid, 'output', f'{pid}.mtl'), 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
lines = [line.replace(texture_filename_end, '.jpg') for line in lines]
|
||||||
|
with open(os.path.join(config.workdir, pid, 'output', f'{pid}.mtl'), 'w') as f:
|
||||||
|
f.writelines(lines)
|
||||||
|
f.close()
|
||||||
|
fix_filename(pid)
|
||||||
|
|
||||||
|
# 统一blender环境
|
||||||
|
reload_obj(pid)
|
||||||
|
|
||||||
|
# 统一模型方向、位置、大小...
|
||||||
|
pid_objname = find_pid_objname(pid)
|
||||||
|
bpy.data.objects[pid_objname].rotation_euler = (0, 0, 0)
|
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
|
||||||
|
bpy.context.object.location[0] = 0
|
||||||
|
bpy.context.object.location[1] = 0
|
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||||
|
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 模型基础校正完成,共费时{libs.diff_time(start_time)}')
|
||||||
|
|
||||||
|
def export_and_update_obj(pid):
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始导出并上传模型...')
|
||||||
|
start_time = time.time()
|
||||||
|
obj_filename = os.path.join(config.workdir, pid, 'output', f'{pid}.obj')
|
||||||
|
bpy.ops.export_scene.obj(filepath=obj_filename)
|
||||||
|
|
||||||
|
# 上传到oss
|
||||||
|
config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/{pid}.obj', os.path.join(config.workdir, pid, 'output', f'{pid}.obj'))
|
||||||
|
config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/{pid}.mtl', os.path.join(config.workdir, pid, 'output', f'{pid}.mtl'))
|
||||||
|
config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/{pid}.jpg', os.path.join(config.workdir, pid, 'output', f'{pid}.jpg'))
|
||||||
|
config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/{pid}.obj.rcInfo', os.path.join(config.workdir, pid, 'output', f'{pid}.obj.rcInfo'))
|
||||||
|
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 模型导出并上传完成,共费时{libs.diff_time(start_time)}')
|
||||||
|
|
||||||
|
def resize_texture_and_reload_obj(pid, ratio=0.5):
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始压缩贴图并重载模型...')
|
||||||
|
start_time = time.time()
|
||||||
|
bpy.ops.wm.quit_blender()
|
||||||
|
|
||||||
|
image_name = os.path.join(config.workdir, pid, 'output', f'{pid}.jpg')
|
||||||
|
img = Image.open(image_name)
|
||||||
|
w, h = img.size
|
||||||
|
img = img.resize((int(w * ratio), int(h * ratio)))
|
||||||
|
img.save(image_name, optimize=True, quality=95)
|
||||||
|
|
||||||
|
reload_obj(pid)
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 贴图压缩并重载模型完成,共费时{libs.diff_time(start_time)}')
|
||||||
|
|
||||||
|
def export_and_update_glbs(pid):
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始导出并上传审核模型和3D相册模型glb文件...')
|
||||||
|
start_time = time.time()
|
||||||
|
headcount = libs.getHeadCount(pid)
|
||||||
|
pid_objname = find_pid_objname(pid)
|
||||||
|
obj = bpy.data.objects[pid_objname]
|
||||||
|
obj.select_set(True)
|
||||||
|
|
||||||
|
model_info = {}
|
||||||
|
model_info['headcount'] = headcount
|
||||||
|
model_info['faces'] = round(len(obj.data.polygons) / 10000)
|
||||||
|
model_info['height'] = round(obj.dimensions.y * 100)
|
||||||
|
|
||||||
|
# bpy.ops.wm.save_as_mainfile(filepath=os.path.join(config.workdir, pid, f'{pid}.blend'))
|
||||||
|
|
||||||
|
# 统一缩放到9cm标准尺寸
|
||||||
|
scale = 90 / bpy.data.objects[pid_objname].dimensions.y
|
||||||
|
bpy.data.objects[pid_objname].scale = (scale, scale, scale)
|
||||||
|
bpy.ops.object.transform_apply(scale=True)
|
||||||
|
|
||||||
|
# bpy.ops.wm.save_as_mainfile(filepath=os.path.join(config.workdir, pid, f'{pid}-9cm.blend'))
|
||||||
|
|
||||||
|
bm = bmesh_copy_from_object(obj)
|
||||||
|
model_info['volume'] = round(bm.calc_volume(), 2)
|
||||||
|
model_info['weight'] = round(model_info['volume'] * 1.226, 2)
|
||||||
|
print(f'{pid}的模型数据:{model_info}')
|
||||||
|
|
||||||
|
res = requests.get(f'{config.urls["upload_model_info_url"]}?pid={pid}&headcount={headcount}&faces={model_info["faces"]}&volume={model_info["volume"]}&weight={model_info["weight"]}&height={model_info["height"]}')
|
||||||
|
print('上传模型数据:', res.text)
|
||||||
|
# with open(os.path.join(config.sharedir, 'model_info', f'{pid}.json'), 'w') as f:
|
||||||
|
# json.dump(model_info, f)
|
||||||
|
# f.close()
|
||||||
|
|
||||||
|
# 先生成审核模型
|
||||||
|
faces_dest = 300000 * headcount
|
||||||
|
# 减面
|
||||||
|
faces_current = len(bpy.data.objects[pid_objname].data.polygons)
|
||||||
|
print(f'当前面数:{faces_current},目标面数:{faces_dest}')
|
||||||
|
|
||||||
|
bpy.ops.object.modifier_add(type='DECIMATE')
|
||||||
|
bpy.context.object.modifiers["Decimate"].ratio = faces_dest / faces_current
|
||||||
|
bpy.ops.object.modifier_apply(modifier="Decimate")
|
||||||
|
|
||||||
|
glb_filename = os.path.join(config.workdir, pid, 'output', f'{pid}.glb')
|
||||||
|
bpy.ops.export_scene.gltf(filepath=glb_filename, export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False)
|
||||||
|
|
||||||
|
# 再生成数字模型
|
||||||
|
faces_dest = 120000 * headcount
|
||||||
|
|
||||||
|
# 减面
|
||||||
|
faces_current = len(bpy.data.objects[pid_objname].data.polygons)
|
||||||
|
print(f'当前面数:{faces_current},目标面数:{faces_dest}')
|
||||||
|
|
||||||
|
bpy.ops.object.modifier_add(type='DECIMATE')
|
||||||
|
bpy.context.object.modifiers["Decimate"].ratio = faces_dest / faces_current
|
||||||
|
bpy.ops.object.modifier_apply(modifier="Decimate")
|
||||||
|
|
||||||
|
glb_filename = os.path.join(config.workdir, pid, 'output', f'{pid}-3d.glb')
|
||||||
|
bpy.ops.export_scene.gltf(filepath=glb_filename, export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False)
|
||||||
|
|
||||||
|
os.system(f'gltfpack -c -i {os.path.join(config.workdir, pid, "output", f"{pid}.glb")} -o {os.path.join(config.workdir, pid, "output", f"{pid}-pack.glb")}')
|
||||||
|
config.oss_bucket.put_object_from_file(f'glbs/auto/{pid}.glb', os.path.join(config.workdir, pid, 'output', f'{pid}-pack.glb'))
|
||||||
|
config.oss_bucket.put_object_from_file(f'glbs/3d/{pid}.glb', os.path.join(config.workdir, pid, 'output', f'{pid}-3d.glb'))
|
||||||
|
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} glb文件导出并上传完成,共费时{libs.diff_time(start_time)}')
|
||||||
|
|
||||||
|
def step3(pid):
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始模型后道处理')
|
||||||
|
start_time = time.time()
|
||||||
|
# 方向、大小、位置等基础校正
|
||||||
|
base_fix(pid)
|
||||||
|
# 去灰
|
||||||
|
|
||||||
|
# 避开人脸白色提纯
|
||||||
|
|
||||||
|
# TODO: 人脸五官特征加深
|
||||||
|
# TODO: 自动UV分割,优先级顺序:脚底、人脸、手指、手臂内侧、大腿内侧、前身、后背、其他
|
||||||
|
# TODO: 根据UV分割自动修贴图
|
||||||
|
# 调用blender生成3D相册glb文件和修模审核glb文件,压缩贴图
|
||||||
|
|
||||||
|
export_and_update_obj(pid)
|
||||||
|
resize_texture_and_reload_obj(pid)
|
||||||
|
export_and_update_glbs(pid)
|
||||||
|
|
||||||
|
# 更新本地任务状态,更新云端任务状态
|
||||||
|
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})
|
||||||
|
print('上传完成更新建模成功状态:', res.text)
|
||||||
|
shutil.rmtree(os.path.join(config.workdir, pid), ignore_errors=True)
|
||||||
|
libs_db.finish_task({"task_type": "make", "task_key": pid})
|
||||||
|
|
||||||
|
def main(pid):
|
||||||
|
if pid == '0':
|
||||||
|
while True:
|
||||||
|
# 取本地mysql队列任务,完成第三步的建模后处理任务
|
||||||
|
|
||||||
|
step3(pid)
|
||||||
|
else:
|
||||||
|
step3(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)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import sys
|
||||||
|
from PyQt5.QtCore import QUrl
|
||||||
|
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
|
||||||
|
from PyQt5.QtWebEngineWidgets import QWebEngineView
|
||||||
|
|
||||||
|
class WebBrowserWindow(QMainWindow):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.browser = QWebEngineView()
|
||||||
|
self.browser.setUrl(QUrl("https://www.qq.com")) # 设置要打开的网页
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(self.browser)
|
||||||
|
|
||||||
|
central_widget = QWidget()
|
||||||
|
central_widget.setLayout(layout)
|
||||||
|
|
||||||
|
self.setCentralWidget(central_widget)
|
||||||
|
self.setWindowTitle("Web Browser")
|
||||||
|
self.setGeometry(100, 100, 800, 600)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
window = WebBrowserWindow()
|
||||||
|
window.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
SELECT *, ABS(TIMESTAMPDIFF(MINUTE , started_at, finished_at)) AS durning from tasks
|
||||||
|
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
import os, oss2, sys, time
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from tqdm import tqdm
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from PIL import Image, ImageEnhance
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
import config, libs
|
||||||
|
|
||||||
|
def ps_color_scale_adjustment(image, shadow=0, highlight=255, midtones=1):
|
||||||
|
'''
|
||||||
|
模拟 PS 的色阶调整; 0 <= Shadow < Highlight <= 255
|
||||||
|
:param image: 传入的图片
|
||||||
|
:param shadow: 黑场(0-Highlight)
|
||||||
|
:param highlight: 白场(Shadow-255)
|
||||||
|
:param midtones: 灰场(9.99-0.01)
|
||||||
|
:return: 图片
|
||||||
|
'''
|
||||||
|
if highlight > 255:
|
||||||
|
highlight = 255
|
||||||
|
if shadow < 0:
|
||||||
|
shadow = 0
|
||||||
|
if shadow >= highlight:
|
||||||
|
shadow = highlight - 2
|
||||||
|
if midtones > 9.99:
|
||||||
|
midtones = 9.99
|
||||||
|
if midtones < 0.01:
|
||||||
|
midtones = 0.01
|
||||||
|
image = np.array(image, dtype=np.float16)
|
||||||
|
# 计算白场 黑场离差
|
||||||
|
Diff = highlight - shadow
|
||||||
|
image = image - shadow
|
||||||
|
image[image < 0] = 0
|
||||||
|
image = (image / Diff) ** (1 / midtones) * 255
|
||||||
|
image[image > 255] = 255
|
||||||
|
image = np.array(image, dtype=np.uint8)
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
|
||||||
|
def show_histogram(image, image_id, save_hist_dir, min_threshold, max_threshold):
|
||||||
|
'''
|
||||||
|
画出直方图展示
|
||||||
|
:param image: 导入图片
|
||||||
|
:param image_id: 图片id编号
|
||||||
|
:param save_hist_dir: 保存路径
|
||||||
|
:param min_threshold: 最小阈值
|
||||||
|
:param max_threshold: 最大阈值
|
||||||
|
:return: 原图image,和裁剪原图直方图高低阈值后的图片image_change
|
||||||
|
'''
|
||||||
|
plt.rcParams['font.family'] = 'SimHei'
|
||||||
|
plt.rcParams['axes.unicode_minus'] = False
|
||||||
|
plt.hist(image.ravel(), 254, range=(2, 256), density=False)
|
||||||
|
plt.hist(image.ravel(), 96, range=(2, 50), density=False) # 放大 range(0, 50),bins值最好是range的两倍,显得更稀疏,便于对比
|
||||||
|
plt.hist(image.ravel(), 110, range=(200, 255), density=False) # 放大 range(225, 255)
|
||||||
|
plt.annotate('thresh1=' + str(min_threshold), # 文本内容
|
||||||
|
xy=(min_threshold, 0), # 箭头指向位置 # 阈值设定值!
|
||||||
|
xytext=(min_threshold, 500000), # 文本位置 # 阈值设定值!
|
||||||
|
arrowprops=dict(facecolor='black', width=1, shrink=5, headwidth=2)) # 箭头
|
||||||
|
plt.annotate('thresh2=' + str(max_threshold), # 文本内容
|
||||||
|
xy=(max_threshold, 0), # 箭头指向位置 # 阈值设定值!
|
||||||
|
xytext=(max_threshold, 500000), # 文本位置 # 阈值设定值!
|
||||||
|
arrowprops=dict(facecolor='black', width=1, shrink=5, headwidth=2)) # 箭头
|
||||||
|
# 在y轴上绘制一条直线
|
||||||
|
# plt.axhline(y=10000, color='r', linestyle='--', linewidth=0.5)
|
||||||
|
plt.title(str(image_id))
|
||||||
|
# plt.show()
|
||||||
|
# 保存直方图
|
||||||
|
save_hist_name = os.path.join(save_hist_dir, f'{image_id}_{min_threshold}&{max_threshold}.jpg')
|
||||||
|
plt.savefig(save_hist_name)
|
||||||
|
# 清空画布, 防止重叠展示
|
||||||
|
plt.clf()
|
||||||
|
|
||||||
|
|
||||||
|
def low_find_histogram_range(image, target_frequency):
|
||||||
|
'''
|
||||||
|
循环查找在 target_frequency (y)频次限制下的直方图区间值(x)
|
||||||
|
:param image: 导入图片
|
||||||
|
:param target_frequency: 直方图 y 频次限制条件
|
||||||
|
:return: 直方图区间 x,和 该区间频次 y
|
||||||
|
'''
|
||||||
|
# 计算灰度直方图
|
||||||
|
hist, bins = np.histogram(image, bins=256, range=[0, 256])
|
||||||
|
# 初始化区间和频次
|
||||||
|
interval = 2
|
||||||
|
frequency = hist[255]
|
||||||
|
while frequency < target_frequency:
|
||||||
|
# 更新区间和频次
|
||||||
|
interval += 1
|
||||||
|
# 检查直方图的频次是否为None,如果频次是None,则将其设为0,这样可以避免将None和int进行比较报错。
|
||||||
|
frequency = hist[interval] if hist[interval] is not None else 0
|
||||||
|
frequency += hist[interval] if hist[interval] is not None else 0
|
||||||
|
print(f'x={interval}, y={frequency}')
|
||||||
|
# 如果频次接近10000则停止循环
|
||||||
|
if target_frequency - 2000 <= frequency <= target_frequency + 1000:
|
||||||
|
break
|
||||||
|
|
||||||
|
return interval, frequency
|
||||||
|
|
||||||
|
|
||||||
|
def high_find_histogram_range(image, target_frequency):
|
||||||
|
'''
|
||||||
|
循环查找在 target_frequency (y)频次限制下的直方图区间值(x)
|
||||||
|
:param image: 导入图片
|
||||||
|
:param target_frequency: 直方图 y 频次限制条件
|
||||||
|
:return: 直方图区间 x,和 该区间频次 y
|
||||||
|
'''
|
||||||
|
# 计算灰度直方图
|
||||||
|
hist, bins = np.histogram(image, bins=256, range=[0, 256])
|
||||||
|
# 初始化区间和频次
|
||||||
|
interval = 255
|
||||||
|
frequency = hist[255]
|
||||||
|
while frequency < target_frequency:
|
||||||
|
# 更新区间和频次
|
||||||
|
interval -= 1
|
||||||
|
# 检查直方图的频次是否为None,如果频次是None,则将其设为0,这样可以避免将None和int进行比较报错。
|
||||||
|
frequency = hist[interval] if hist[interval] is not None else 0
|
||||||
|
frequency += hist[interval] if hist[interval] is not None else 0
|
||||||
|
# 如果频次接近10000则停止循环
|
||||||
|
if target_frequency - 2000 <= frequency <= target_frequency + 2000:
|
||||||
|
break
|
||||||
|
|
||||||
|
return interval, frequency
|
||||||
|
|
||||||
|
|
||||||
|
def sharpening_filter(image):
|
||||||
|
'''
|
||||||
|
锐化滤波器对图片进行锐化,增强图像中的边缘和细节
|
||||||
|
:param image: 导入图片
|
||||||
|
:return: 锐化后的图片
|
||||||
|
'''
|
||||||
|
# sharp_kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
|
||||||
|
sharp_kernel = np.array([[0, -0.5, 0], [-0.5, 3, -0.5], [0, -0.5, 0]])
|
||||||
|
sharpened_image = cv2.filter2D(image, -1, sharp_kernel)
|
||||||
|
return sharpened_image
|
||||||
|
|
||||||
|
def reduce_sharpness(image, factor):
|
||||||
|
'''
|
||||||
|
使用PIL库减弱图像锐度
|
||||||
|
:param image: 图像
|
||||||
|
:param factor: 锐度因子,0表示最大程度减弱锐度,1表示原始图像
|
||||||
|
:return: 减弱锐度后的图像
|
||||||
|
'''
|
||||||
|
# OpenCV 格式的图像转换为 PIL 的 Image 对象
|
||||||
|
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||||
|
pil_image = Image.fromarray(image_rgb)
|
||||||
|
enhancer = ImageEnhance.Sharpness(pil_image)
|
||||||
|
reduced_image = enhancer.enhance(factor)
|
||||||
|
# PIL 的 Image 对象转换为 OpenCV 的图像格式
|
||||||
|
image_array = np.array(reduced_image)
|
||||||
|
sharpened_image = cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR)
|
||||||
|
|
||||||
|
return sharpened_image
|
||||||
|
|
||||||
|
def find_last_x(image, slope_threshold = 1000):
|
||||||
|
x = []
|
||||||
|
y = []
|
||||||
|
hist, bins = np.histogram(image, bins=256, range=[0, 256])
|
||||||
|
for i in range(2, 50):
|
||||||
|
x.append(i)
|
||||||
|
y.append(hist[i])
|
||||||
|
slopes = [abs(y[i + 1] - y[i]) for i in range(len(x) - 1)]
|
||||||
|
|
||||||
|
current_interval = []
|
||||||
|
max_interval = []
|
||||||
|
max_x = {}
|
||||||
|
for i, slope in enumerate(slopes):
|
||||||
|
current_interval.append(slope)
|
||||||
|
if slope >= slope_threshold:
|
||||||
|
if len(current_interval) > len(max_interval):
|
||||||
|
max_interval = current_interval.copy()
|
||||||
|
max_x[x[i]] = slope
|
||||||
|
current_interval = []
|
||||||
|
|
||||||
|
print(max_x)
|
||||||
|
last_x = list(max_x)[-1]
|
||||||
|
last_y = max_x[last_x]
|
||||||
|
return last_x, last_y
|
||||||
|
|
||||||
|
def main(pid, print_id):
|
||||||
|
texture_filename = f'{input_dir}{pid}Tex1.{print_id}.jpg'
|
||||||
|
input_image = cv2.imread(texture_filename)
|
||||||
|
# low_x_thresh, low_y_frequency = low_find_histogram_range(input_image, low_y_limit)
|
||||||
|
low_x_thresh, low_y_frequency = find_last_x(input_image, 1000)
|
||||||
|
high_x_thresh, high_y_frequency = high_find_histogram_range(input_image, high_y_limit)
|
||||||
|
print(f"{low_x_thresh} 区间, {low_y_frequency} 频次")
|
||||||
|
print(f"{high_x_thresh} 区间, {high_y_frequency} 频次")
|
||||||
|
high_output_image = ps_color_scale_adjustment(input_image, shadow=low_x_thresh, highlight=high_x_thresh, midtones=1)
|
||||||
|
# high_output_image = ps_color_scale_adjustment(low_ouput_image, shadow=0, highlight=high_x_thresh, midtones=1)
|
||||||
|
|
||||||
|
# 人体贴图和黑色背景交界处不进行锐化
|
||||||
|
gray = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
|
||||||
|
_, thresh = cv2.threshold(gray, 2, 255, cv2.THRESH_BINARY)
|
||||||
|
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
|
||||||
|
gradient = cv2.morphologyEx(thresh, cv2.MORPH_GRADIENT, kernel)
|
||||||
|
roi_gradient = cv2.bitwise_and(high_output_image, high_output_image, mask=gradient)
|
||||||
|
|
||||||
|
# 锐化滤波器
|
||||||
|
# sharpened_image = sharpening_filter(high_output_image)
|
||||||
|
sharpened_image = reduce_sharpness(high_output_image, factor=4)
|
||||||
|
# 将原图边界替换锐化后的图片边界
|
||||||
|
sharpened_image[gradient != 0] = roi_gradient[gradient != 0]
|
||||||
|
|
||||||
|
# 直方图标记并保存
|
||||||
|
# show_histogram(input_image, img_id, low_x_thresh, high_x_thresh)
|
||||||
|
cv2.imwrite(texture_filename, sharpened_image, [cv2.IMWRITE_JPEG_QUALITY, 95]) # 保存图片的质量是原图的 95%
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
input_dir = "D:\\AI_pycharm\\Change_grayscale\\Texture_photos\\original_images\\"
|
||||||
|
save_dir = "D:\\AI_pycharm\\Change_grayscale\\Texture_photos\\test\\"
|
||||||
|
|
||||||
|
low_y_limit = 48000
|
||||||
|
high_y_limit = 13000
|
||||||
|
|
||||||
|
input_dir = '/data/datasets/texure_photos/'
|
||||||
|
|
||||||
|
pids = '99724,99747,99762,99763,99777,99778,99807,99812,99823,99843,99405,99416,97984,97662,86153'
|
||||||
|
for pid in pids.split(','):
|
||||||
|
pid, print_id = pid.split('_')
|
||||||
|
# 根据前缀获取文件列表
|
||||||
|
path = f'/data/datasets/texure_photos/'
|
||||||
|
texture_filename = f'{path}{pid}Tex1.{print_id}.jpg'
|
||||||
|
|
||||||
|
prefix = f'objs/print/{pid}/'
|
||||||
|
if config.oss_bucket.object_exists(f'{prefix}{pid}Tex1.{print_id}.jpg'):
|
||||||
|
print(f'{pid}Tex1.{print_id}.jpg 处理中...')
|
||||||
|
if not os.path.exists(texture_filename):
|
||||||
|
os.makedirs(path, exist_ok=True)
|
||||||
|
config.oss_bucket.get_object_to_file(f'{prefix}{pid}Tex1.{print_id}.jpg', texture_filename)
|
||||||
|
main(pid, print_id)
|
||||||
|
else:
|
||||||
|
print(f'文件已存在,直接上传')
|
||||||
|
config.oss_bucket.put_object_from_file(f'objs/print/{pid}/{pid}Tex1.{print_id}.jpg', texture_filename)
|
||||||
|
|
||||||
|
print(f'{pid}Tex1.{print_id}.jpg 处理完成')
|
||||||
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import win32gui, win32con, time, os, sys
|
||||||
|
import pyautogui as ag
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
import config, libs
|
||||||
|
|
||||||
|
def find_and_maximize_window(window_title):
|
||||||
|
windows = []
|
||||||
|
win32gui.EnumWindows(lambda hwnd, windows: windows.append(hwnd), windows)
|
||||||
|
|
||||||
|
for hwnd in windows:
|
||||||
|
if win32gui.IsWindowVisible(hwnd):
|
||||||
|
if window_title in win32gui.GetWindowText(hwnd):
|
||||||
|
print(f'found {window_title} hwnd:{hwnd}')
|
||||||
|
# win32gui.ShowWindow(hwnd, win32con.SW_MAXIMIZE)
|
||||||
|
win32gui.SetForegroundWindow(hwnd)
|
||||||
|
pid = win32gui.GetWindowText(hwnd).split('wait')[0].split(' ')[0].split('-')[0].split('*')[0]
|
||||||
|
left, top, right, bottom = win32gui.GetWindowRect(hwnd)
|
||||||
|
return pid, left, top, right, bottom
|
||||||
|
return '0', 0, 0, 0, 0
|
||||||
|
|
||||||
|
def get_defineDistances(pid, left, top, right, bottom):
|
||||||
|
psid = libs.getPSid(pid)
|
||||||
|
distances = config.ps_floor_sticker.get(psid, config.ps_floor_sticker['default'])
|
||||||
|
for index, d in enumerate(distances.split(';')):
|
||||||
|
p1, p2, distance = d.split(' ')
|
||||||
|
if index == 0:
|
||||||
|
ag.moveTo(left + 80, top + 290)
|
||||||
|
else:
|
||||||
|
ag.moveTo(left + 80, top + 290 + 15) # Create distance line height 15
|
||||||
|
ag.click()
|
||||||
|
|
||||||
|
ag.moveTo(left + 302, (bottom - 100)) # A point
|
||||||
|
ag.click();repeat_backspace(20)
|
||||||
|
ag.typewrite(p1)
|
||||||
|
|
||||||
|
ag.moveTo(left + 302, (bottom - 80)) # B point
|
||||||
|
ag.click();repeat_backspace(20)
|
||||||
|
ag.typewrite(p2)
|
||||||
|
|
||||||
|
ag.moveTo(left + 302, (bottom - 35)) # Definded distance
|
||||||
|
ag.click();repeat_backspace(8)
|
||||||
|
ag.typewrite(distance)
|
||||||
|
ag.press('enter')
|
||||||
|
|
||||||
|
|
||||||
|
def repeat_backspace(times):
|
||||||
|
for i in range(times):
|
||||||
|
ag.press('backspace')
|
||||||
|
for i in range(times):
|
||||||
|
ag.press('delete')
|
||||||
|
|
||||||
|
def defind_distance(pid, left, top, right, bottom):
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 开始定义定位点距离...')
|
||||||
|
print(f'left: {left}, top: {top}, right: {right}, bottom: {bottom}')
|
||||||
|
# ag.PAUSE = 1
|
||||||
|
|
||||||
|
ag.moveTo(left + 20, top + 200) # open Control points
|
||||||
|
ag.click()
|
||||||
|
|
||||||
|
get_defineDistances(pid, left, top, right, bottom)
|
||||||
|
|
||||||
|
ag.hotkey('ctrl', 's') # save project
|
||||||
|
ag.hotkey('alt', 'f4') # close project
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 定义定位点距离完成')
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
while True:
|
||||||
|
time.sleep(1)
|
||||||
|
title = "wait"
|
||||||
|
pid, left, top, right, bottom = find_and_maximize_window(title)
|
||||||
|
if pid == '0':
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 找到{pid}的定位点距离定义窗口,开始定位点距离定义...')
|
||||||
|
start_time = time.time()
|
||||||
|
defind_distance(pid, left, top, right, bottom)
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} {pid} 定位点距离定义完成,共费时{libs.diff_time(start_time)}')
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import os, sys, time, bpy, bmesh, shutil
|
||||||
|
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 bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False):
|
||||||
|
"""Returns a transformed, triangulated copy of the mesh"""
|
||||||
|
assert obj.type == 'MESH'
|
||||||
|
if apply_modifiers and obj.modifiers:
|
||||||
|
import bpy
|
||||||
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
|
obj_eval = obj.evaluated_get(depsgraph)
|
||||||
|
me = obj_eval.to_mesh()
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(me)
|
||||||
|
obj_eval.to_mesh_clear()
|
||||||
|
else:
|
||||||
|
me = obj.data
|
||||||
|
if obj.mode == 'EDIT':
|
||||||
|
bm_orig = bmesh.from_edit_mesh(me)
|
||||||
|
bm = bm_orig.copy()
|
||||||
|
else:
|
||||||
|
bm = bmesh.new()
|
||||||
|
bm.from_mesh(me)
|
||||||
|
if transform:
|
||||||
|
matrix = obj.matrix_world.copy()
|
||||||
|
if not matrix.is_identity:
|
||||||
|
bm.transform(matrix)
|
||||||
|
matrix.translation.zero()
|
||||||
|
if not matrix.is_identity:
|
||||||
|
bm.normal_update()
|
||||||
|
if triangulate:
|
||||||
|
bmesh.ops.triangulate(bm, faces=bm.faces)
|
||||||
|
return bm
|
||||||
|
|
||||||
|
def reload_obj(pid, action):
|
||||||
|
obj_filename = os.path.join(config.workdir, action, pid, f'{pid}.obj')
|
||||||
|
bpy.ops.wm.read_homefile()
|
||||||
|
bpy.ops.object.delete(use_global=False, confirm=False)
|
||||||
|
bpy.ops.import_scene.obj(filepath=obj_filename)
|
||||||
|
bpy.context.scene.unit_settings.scale_length = 0.001
|
||||||
|
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)
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def cal_weight(obj, size):
|
||||||
|
# 统一缩放到9cm标准尺寸
|
||||||
|
scale = size / obj.dimensions.z
|
||||||
|
obj.scale = (scale, scale, scale)
|
||||||
|
bpy.ops.object.transform_apply(scale=True)
|
||||||
|
|
||||||
|
# bpy.ops.wm.save_as_mainfile(filepath=os.path.join(config.workdir, action, pid, f'{pid}-{size/10}cm.blend'))
|
||||||
|
model_info = {}
|
||||||
|
bm = bmesh_copy_from_object(obj)
|
||||||
|
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')
|
||||||
|
|
||||||
|
def main(action, pid, sizes):
|
||||||
|
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}的体积与克重估算信息:')
|
||||||
|
for size in sizes:
|
||||||
|
size = float(size)
|
||||||
|
cal_weight(obj, size)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sizes = (90, 120, 150, 180)
|
||||||
|
|
||||||
|
if len(sys.argv) == 3:
|
||||||
|
action = sys.argv[1]
|
||||||
|
pids = sys.argv[2].split(',')
|
||||||
|
for pid in pids:
|
||||||
|
main(action, pid, sizes)
|
||||||
|
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 <pids>')
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import os, sys, datetime, subprocess, shlex, shutil, requests, time, 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 down_xmps_from_oss(psid):
|
||||||
|
def need_down():
|
||||||
|
filename = f'xmps/{psid}/{psid}.rcbox'
|
||||||
|
if not config.oss_bucket.object_exists(filename):
|
||||||
|
print(f'未找到{filename}, 请检查是否已经上传{psid}号影棚坐标文件')
|
||||||
|
time.sleep(5)
|
||||||
|
return False
|
||||||
|
oss_file_last_modified = float(config.oss_bucket.get_object_meta(filename).last_modified)
|
||||||
|
local_file = os.path.join(config_path, 'xmps', psid, f'{psid}.rcbox')
|
||||||
|
if not os.path.exists(local_file):
|
||||||
|
print(f'未找到{local_file}, 需要下载影棚坐标文件')
|
||||||
|
os.makedirs(os.path.join(config_path, 'xmps', psid), exist_ok=True)
|
||||||
|
config.oss_bucket.get_object_to_file(filename, local_file)
|
||||||
|
return True
|
||||||
|
local_file_last_modified = os.path.getmtime(local_file)
|
||||||
|
if oss_file_last_modified > local_file_last_modified:
|
||||||
|
print(f'本地{local_file}文件已过期, 需要下载影棚坐标文件')
|
||||||
|
config.oss_bucket.get_object_to_file(filename, local_file)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not need_down(): return
|
||||||
|
|
||||||
|
print('正在下载影棚坐标文件')
|
||||||
|
dest_path = os.path.join(config_path, 'xmps', psid)
|
||||||
|
|
||||||
|
if os.path.exists(os.path.join(dest_path)):
|
||||||
|
shutil.rmtree(os.path.join(dest_path), ignore_errors=True)
|
||||||
|
|
||||||
|
os.makedirs(os.path.join(dest_path, 'mesh'), exist_ok=True)
|
||||||
|
os.makedirs(os.path.join(dest_path, 'texture'), exist_ok=True)
|
||||||
|
|
||||||
|
prefix = f'xmps/{psid}/mesh/'
|
||||||
|
filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix)
|
||||||
|
for file in filelist:
|
||||||
|
if file.key.endswith('.xmp'):
|
||||||
|
filename = file.key.split('/')[-1]
|
||||||
|
print('正在下载:', file.key)
|
||||||
|
config.oss_bucket.get_object_to_file(file.key, os.path.join(dest_path, 'mesh', filename))
|
||||||
|
|
||||||
|
prefix = f'xmps/{psid}/texture/'
|
||||||
|
filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix)
|
||||||
|
for file in filelist:
|
||||||
|
if file.key.endswith('.xmp'):
|
||||||
|
filename = file.key.split('/')[-1]
|
||||||
|
print('正在下载:', file.key)
|
||||||
|
config.oss_bucket.get_object_to_file(file.key, os.path.join(dest_path, 'texture', filename))
|
||||||
|
|
||||||
|
print('下载完成')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
start = datetime.datetime.now()
|
||||||
|
if len(sys.argv) == 2:
|
||||||
|
pids = sys.argv[1]
|
||||||
|
else:
|
||||||
|
print('usage: python downxmps.py [pids]')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
for pid in pids.split(','):
|
||||||
|
input_path = os.path.join('d:\\', pid)
|
||||||
|
ImagesGeometry = os.path.join(input_path, "photo1")
|
||||||
|
ImagesTexture = os.path.join(input_path, "photo2")
|
||||||
|
|
||||||
|
psid = libs.getPSid(pid)
|
||||||
|
config_path = f'D:\\apps\\config\\'
|
||||||
|
xmps_mesh_path = config_path + f'xmps\\{psid}\\mesh\\'
|
||||||
|
xmps_texture_path = config_path + f'xmps\\{psid}\\texture\\'
|
||||||
|
|
||||||
|
down_xmps_from_oss(psid)
|
||||||
|
|
||||||
|
os.system('xcopy /y /q ' + xmps_mesh_path + '*.* ' + ImagesGeometry)
|
||||||
|
os.system('xcopy /y /q ' + xmps_texture_path + '*.* ' + ImagesTexture)
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import os, sys, time, shutil, subprocess, shlex, json
|
||||||
|
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文件所在目录...')
|
||||||
|
config.oss_bucket.delete_object(f'xmps/{pid}/')
|
||||||
|
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):
|
||||||
|
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)
|
||||||
|
|
||||||
|
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 "D:\\make2\\config\\exportXMP.config.xml" \
|
||||||
|
-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(config.ps_floor_sticker.get(psid, config.ps_floor_sticker["default"]))} -align -align -update {config.r2["setRegion"]} \
|
||||||
|
-exportXMP "D:\\make2\\config\\exportXMP.config.xml" \
|
||||||
|
-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相机位姿质量评估
|
||||||
|
upload_or_not = input('是否上传oss?(y/n)')
|
||||||
|
if upload_or_not == 'y':
|
||||||
|
upload_xmp(pid)
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
print(f'useage: python {sys.argv[0]} pid1,pid2,pid3')
|
||||||
|
sys.exit(1)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import redis, os, sys
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
import config, libs
|
||||||
|
|
||||||
|
def main(cmd, order_id):
|
||||||
|
if cmd == 'print':
|
||||||
|
key = 'model:printOrder'
|
||||||
|
elif cmd == 'repair':
|
||||||
|
key = 'model:IndependentRepairTeamcheckGLBQueue'
|
||||||
|
elif cmd == 'make3d':
|
||||||
|
key = 'model:make'
|
||||||
|
elif cmd == 'make3d10':
|
||||||
|
key = 'model:make10'
|
||||||
|
elif cmd == 'foot':
|
||||||
|
key = 'model:foot'
|
||||||
|
|
||||||
|
if order_id == 'view':
|
||||||
|
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)}')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) == 3:
|
||||||
|
cmd = sys.argv[1]
|
||||||
|
order_id = sys.argv[2]
|
||||||
|
else:
|
||||||
|
print('用法:python push_cmd.py <cmd> <order_id>')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
r = config.redis_local
|
||||||
|
|
||||||
|
main(cmd, order_id)
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import os, sys, bpy, time
|
||||||
|
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_3d(pid):
|
||||||
|
start_time = time.time()
|
||||||
|
workdir = os.path.join(config.workdir, '3d')
|
||||||
|
|
||||||
|
glb_filename = os.path.join(workdir, pid, f'{pid}_decimate.glb')
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {glb_filename} 开始处理...')
|
||||||
|
if os.path.exists(glb_filename):
|
||||||
|
config.oss_bucket.put_object_from_file(f'glbs/3d/{pid}.glb', glb_filename)
|
||||||
|
# break
|
||||||
|
os.remove(glb_filename)
|
||||||
|
else:
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {glb_filename} 文件不存在,跳过')
|
||||||
|
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} glb文件上传完成,共费时{time.time() - start_time}秒')
|
||||||
|
|
||||||
|
def upload_xmp(pid):
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 上传xmp文件之前先删除oss上的xmp文件所在目录...')
|
||||||
|
config.oss_bucket.delete_object(f'xmps/{pid}/')
|
||||||
|
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():
|
||||||
|
pass
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) == 3:
|
||||||
|
action = sys.argv[1]
|
||||||
|
pids = sys.argv[2]
|
||||||
|
else:
|
||||||
|
print(f'useage: python {sys.argv[0]} [3d|xmp] pid1,pid2,pid3')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for pid in pids.split(','):
|
||||||
|
if action == '3d':
|
||||||
|
upload_3d(pid)
|
||||||
|
elif action == 'xmp':
|
||||||
|
upload_xmp(pid)
|
||||||
Reference in new issue
Block a user