Compare commits
4
Commits
39c00b4386
...
5c1d4fc1b9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c1d4fc1b9 | ||
|
|
eeb3c85347 | ||
|
|
88d3e90ce3 | ||
|
|
1f6f16aff6 |
No files matched your search
Binary file not shown.
@@ -0,0 +1,40 @@
|
|||||||
|
[account]
|
||||||
|
appId="1nt7j0w17ecd9duruc48m7050012r5a8"
|
||||||
|
appSecret="1nt7j0w17ecd9duruc48m70600m2a32r"
|
||||||
|
shopId="1"
|
||||||
|
|
||||||
|
[oss]
|
||||||
|
AccessKeyId = "LTAI5tSReWm8hz7dSYxxth8f"
|
||||||
|
AccessKeySecret = "8ywTDF9upPAtvgXtLKALY2iMYHIxdS"
|
||||||
|
EndPoint = "oss-cn-shanghai.aliyuncs.com"
|
||||||
|
BucketSuwa3dDataSecret = "suwa3d-securedata"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
#请求的域名
|
||||||
|
#test_domain="http://127.0.0.1:8188/"
|
||||||
|
domain = "https://open.api.suwa3d.com/"
|
||||||
|
#Token地址
|
||||||
|
getToken="token/getToken"
|
||||||
|
#获取pid
|
||||||
|
getPid="link/pid"
|
||||||
|
#记录拍照订单信息
|
||||||
|
recordPose="link/recordBaseInfo"
|
||||||
|
#脸部图片的上传地址
|
||||||
|
faceUploadUrl = "upload/getUploadUrlForBadge"
|
||||||
|
#下单接口
|
||||||
|
order = "order/confirmCartAndOrder"
|
||||||
|
#提交建模
|
||||||
|
submitModeling = "modeling/submitOrderModeling"
|
||||||
|
#模拟图片上传回调
|
||||||
|
callbackImageUpload = "callback/uploadBadgeImage"
|
||||||
|
|
||||||
|
|
||||||
|
[customer]
|
||||||
|
id=1
|
||||||
|
peoples=1
|
||||||
|
props=0
|
||||||
|
pets=0
|
||||||
|
people_pose=["站姿"]
|
||||||
|
platform=99
|
||||||
|
customer_name="董长希",
|
||||||
|
customer_mobile="18950403426"
|
||||||
+208
@@ -0,0 +1,208 @@
|
|||||||
|
import utils,time,os,sys
|
||||||
|
from utils import cfg
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#获取PID,记录基础信息
|
||||||
|
def getPidRecordPose():
|
||||||
|
params = {
|
||||||
|
"photo_group_id":int(time.time()),
|
||||||
|
"customer_id":cfg("customer.id"),
|
||||||
|
"platform":cfg("customer.platform"),
|
||||||
|
}
|
||||||
|
url = cfg("api.domain")+cfg("api.getPid")
|
||||||
|
data,status = utils.requestUrl(url,"GET",params)
|
||||||
|
if status != "success":
|
||||||
|
return 0,"获取PID失败"
|
||||||
|
pid = data["pid"]
|
||||||
|
|
||||||
|
#记录基础信息
|
||||||
|
params = {
|
||||||
|
"pid":pid,
|
||||||
|
"shop_id":cfg("account.shopId"),
|
||||||
|
"peoples":cfg("customer.peoples"),
|
||||||
|
"props":cfg("customer.props"),
|
||||||
|
"pets":cfg("customer.pets"),
|
||||||
|
"people_pose":cfg("customer.people_pose"),
|
||||||
|
}
|
||||||
|
url = cfg("api.domain")+cfg("api.recordPose")
|
||||||
|
data,status = utils.requestUrl(url,"POST",params)
|
||||||
|
if status != "success":
|
||||||
|
return 0,"记录基础信息失败"
|
||||||
|
print(f"记录基础信息成功,PID:{pid}")
|
||||||
|
return pid,"success"
|
||||||
|
|
||||||
|
|
||||||
|
#获取照片上传的地址, 上传到脸部位置,上传到卡通图片的位置,
|
||||||
|
def uploadImages(pid,imagePath):
|
||||||
|
|
||||||
|
#检测文件图片是否存在
|
||||||
|
if not os.path.exists(imagePath):
|
||||||
|
return "文件不存在"
|
||||||
|
|
||||||
|
|
||||||
|
ossFacePath = f"photos/{pid}/cartoon/origin/face.jpg"
|
||||||
|
ossPath = f"photos/{pid}/cartoon/cartoon_image/1.png"
|
||||||
|
utils.oss().put_object_from_file(ossFacePath,imagePath)
|
||||||
|
utils.oss().put_object_from_file(ossPath,imagePath)
|
||||||
|
callbackImageUpload(pid)
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
#下单
|
||||||
|
def makeOrder(pid):
|
||||||
|
if pid == 0:
|
||||||
|
return "PID不能为0"
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"pid":pid,
|
||||||
|
"shop_id":cfg("account.shopId"),
|
||||||
|
"address":{
|
||||||
|
"country_id":350206,
|
||||||
|
"province_id":350000,
|
||||||
|
"county_id":"0",
|
||||||
|
"city_id":350200,
|
||||||
|
"address":"汇金湖里大厦B栋312",
|
||||||
|
"receive_address":"汇金湖里大厦B栋312",
|
||||||
|
"send_model_type": 2,
|
||||||
|
"memo": "测试",
|
||||||
|
"customer_name": cfg("customer.name"),
|
||||||
|
"customer_mobile": cfg("customer.mobile")
|
||||||
|
},
|
||||||
|
"order_params": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"discount_name": "",
|
||||||
|
"discount_amount": 0,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"carts_info": [
|
||||||
|
{
|
||||||
|
"prod_id": 7,
|
||||||
|
"skus": [
|
||||||
|
{
|
||||||
|
"quantity": 1,
|
||||||
|
"attrs": [
|
||||||
|
{
|
||||||
|
"attr_id": 5,
|
||||||
|
"attr_val_id": 13,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"attr_id": 1,
|
||||||
|
"attr_val_id": 1,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cartoon_pic_no": 1,
|
||||||
|
"product_name": "badge",
|
||||||
|
"platform": 99
|
||||||
|
}
|
||||||
|
url = cfg("api.domain")+cfg("api.order")
|
||||||
|
data,status = utils.requestUrl(url,"POST",params)
|
||||||
|
print(f"下订单结果:{data}")
|
||||||
|
|
||||||
|
if status != "success":
|
||||||
|
return 0,"下单失败"
|
||||||
|
|
||||||
|
status = submitModeling(data["order_ids"][0])
|
||||||
|
if status != "success":
|
||||||
|
return 0,"提交建模失败"
|
||||||
|
return data["order_ids"][0],"success"
|
||||||
|
|
||||||
|
#图片上传回调
|
||||||
|
def callbackImageUpload(pid):
|
||||||
|
params = {
|
||||||
|
"pid":pid,
|
||||||
|
"token":utils.getToken(),
|
||||||
|
}
|
||||||
|
url = cfg("api.domain")+cfg("api.callbackImageUpload")
|
||||||
|
data,status = utils.requestUrl(url,"GET",params)
|
||||||
|
print(f"图片上传回调结果:{data}-{status}")
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
|
||||||
|
#提交建模
|
||||||
|
def submitModeling(orderId):
|
||||||
|
if orderId == 0:
|
||||||
|
return "订单ID不能为0"
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"order_id":orderId,
|
||||||
|
"is_cartoon":1,
|
||||||
|
}
|
||||||
|
url = cfg("api.domain")+cfg("api.submitModeling")
|
||||||
|
data,status = utils.requestUrl(url,"POST",params)
|
||||||
|
print(f"提交建模结果:{status}")
|
||||||
|
if status != "success":
|
||||||
|
return "提交建模失败"
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
|
||||||
|
def imageFolder():
|
||||||
|
|
||||||
|
#判断目录下是否有images文件夹 和 文件
|
||||||
|
if not os.path.exists("images"):
|
||||||
|
print("images目录不存在")
|
||||||
|
return
|
||||||
|
arrFiles = os.listdir("images")
|
||||||
|
if len(arrFiles) == 0:
|
||||||
|
print("images目录下没有文件")
|
||||||
|
return
|
||||||
|
|
||||||
|
#遍历 images 目录下的所有图片
|
||||||
|
for image in arrFiles:
|
||||||
|
#获取完整的图片地址
|
||||||
|
imagePath = os.path.join("images",image)
|
||||||
|
main(imagePath)
|
||||||
|
|
||||||
|
|
||||||
|
def main(imagePath = ""):
|
||||||
|
|
||||||
|
#判断文件是否存在
|
||||||
|
if not os.path.exists(imagePath):
|
||||||
|
print(f"图片不存在:{imagePath}")
|
||||||
|
return None,False
|
||||||
|
|
||||||
|
|
||||||
|
print(f"开始处理图片:{imagePath}")
|
||||||
|
pid,status = getPidRecordPose()
|
||||||
|
if status != "success":
|
||||||
|
print(f"获取PID失败:{imagePath}")
|
||||||
|
return None,False
|
||||||
|
|
||||||
|
status = uploadImages(pid,imagePath)
|
||||||
|
if status != "success":
|
||||||
|
print(f"上传图片失败:{imagePath}")
|
||||||
|
return None,False
|
||||||
|
|
||||||
|
order_id,status = makeOrder(pid)
|
||||||
|
if status != "success":
|
||||||
|
print(f"下单失败:{imagePath}")
|
||||||
|
return None,False
|
||||||
|
|
||||||
|
data = {"pid":pid,"imagePath":imagePath,"orderId":order_id}
|
||||||
|
print(f"下单成功:{data},移除图片:{imagePath}")
|
||||||
|
os.remove(imagePath)
|
||||||
|
return data,True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
#获取请求参数
|
||||||
|
args = sys.argv[1:]
|
||||||
|
if len(args) == 0:
|
||||||
|
#需要二次确认是否要处理images目录下的所有图片
|
||||||
|
confirmV = input("是否要处理images目录下的所有图片? (y/n)")
|
||||||
|
if confirmV == "y":
|
||||||
|
imageFolder()
|
||||||
|
else:
|
||||||
|
print("请输入图片路径")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
for imagePath in args:
|
||||||
|
main(imagePath)
|
||||||
|
print(f"{args}")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
xdezmm0291gd9x7z89yj3um2709r55mu|1747365162
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import toml,time,hmac,hashlib,requests
|
||||||
|
import oss2,os
|
||||||
|
#读取配置文件
|
||||||
|
def cfg(keyName):
|
||||||
|
try:
|
||||||
|
with open('config.toml', 'r') as f:
|
||||||
|
config = toml.load(f)
|
||||||
|
|
||||||
|
if "." in keyName:
|
||||||
|
keyName = keyName.split(".")
|
||||||
|
return config[keyName[0]][keyName[1]]
|
||||||
|
else:
|
||||||
|
return config[keyName]
|
||||||
|
except Exception as e:
|
||||||
|
return None
|
||||||
|
|
||||||
|
#生成签名
|
||||||
|
def sign(params):
|
||||||
|
params = [
|
||||||
|
f"appId={params['appId']}",
|
||||||
|
f"appSecret={cfg('account.appSecret')}",
|
||||||
|
f"shopId={params['shop_id']}",
|
||||||
|
f"timestamp={params['timestamp']}"
|
||||||
|
]
|
||||||
|
# 拼接参数字符串
|
||||||
|
joined_str = "&".join(params)
|
||||||
|
#进行签名 签名方式采用 HMAC-SHA256 算法
|
||||||
|
hmac_sha256 = hmac.new(cfg("account.appSecret").encode(), joined_str.encode(), hashlib.sha256)
|
||||||
|
sign = hmac_sha256.hexdigest()
|
||||||
|
return sign
|
||||||
|
|
||||||
|
|
||||||
|
#获取token
|
||||||
|
def getToken():
|
||||||
|
#判断是否有token.txt文件
|
||||||
|
if os.path.exists("token.txt"):
|
||||||
|
#读取token.txt文件
|
||||||
|
with open("token.txt", "r") as f:
|
||||||
|
token = f.read()
|
||||||
|
#判断token是否过期
|
||||||
|
if int(token.split("|")[1]) > int(time.time()):
|
||||||
|
return token.split("|")[0]
|
||||||
|
|
||||||
|
#获取token
|
||||||
|
url = cfg("api.domain")+cfg("api.getToken")
|
||||||
|
params = {
|
||||||
|
"appId":cfg("account.appId"),
|
||||||
|
"shop_id":cfg("account.shopId"),
|
||||||
|
"timestamp":str(int(time.time())), #时间戳,秒级 Unix 时间戳(自 1970-01-01 00:00:00 UTC 以来的秒数), 10位数
|
||||||
|
}
|
||||||
|
params["sign"] = sign(params)
|
||||||
|
#发送请求
|
||||||
|
response = requests.post(url, json=params)
|
||||||
|
data = response.json()
|
||||||
|
if data["code"] != 1000:
|
||||||
|
print("获取token失败",data)
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
#记录token
|
||||||
|
with open("token.txt", "w") as f:
|
||||||
|
f.write(data["data"]["token"]+"|"+str(int(time.time())+3600))
|
||||||
|
return data["data"]["token"]
|
||||||
|
|
||||||
|
|
||||||
|
def requestUrl(url,method,params):
|
||||||
|
#获取token
|
||||||
|
token = getToken()
|
||||||
|
#设置headers
|
||||||
|
headers = {
|
||||||
|
"Authorization":f"Bearer {token}",
|
||||||
|
"Content-Type":"application/json"
|
||||||
|
}
|
||||||
|
#发送请求
|
||||||
|
response = requests.request(method,url,headers=headers,json=params)
|
||||||
|
data = response.json()
|
||||||
|
if data["code"] != 1000:
|
||||||
|
print("请求失败",data)
|
||||||
|
return "","error"
|
||||||
|
else:
|
||||||
|
return data["data"],"success"
|
||||||
|
|
||||||
|
def oss():
|
||||||
|
AccessKeyId = cfg("oss.AccessKeyId")
|
||||||
|
AccessKeySecret = cfg("oss.AccessKeySecret")
|
||||||
|
Endpoint = cfg("oss.EndPoint")
|
||||||
|
Bucket = cfg("oss.BucketSuwa3dDataSecret")
|
||||||
|
oss_client = oss2.Bucket(oss2.Auth(AccessKeyId, AccessKeySecret), Endpoint, Bucket)
|
||||||
|
return oss_client
|
||||||
@@ -86,9 +86,8 @@ def rander_image_and_check(args):
|
|||||||
"""渲染图片"""
|
"""渲染图片"""
|
||||||
# 列出obj列表
|
# 列出obj列表
|
||||||
pid = args.pid
|
pid = args.pid
|
||||||
pid_file_dir = os.path.join(args.input)
|
pid_file_dir = os.path.join(args.input,pid)
|
||||||
if not os.path.exists(pid_file_dir):
|
if not os.path.exists(pid_file_dir):
|
||||||
print(f"{pid_file_dir} 文件夹不存在")
|
|
||||||
return
|
return
|
||||||
obj_file_list = [aa for aa in os.listdir(pid_file_dir) if aa.endswith(".obj")]
|
obj_file_list = [aa for aa in os.listdir(pid_file_dir) if aa.endswith(".obj")]
|
||||||
if len(obj_file_list)==0:
|
if len(obj_file_list)==0:
|
||||||
@@ -104,7 +103,6 @@ def rander_image_and_check(args):
|
|||||||
is_rander = False
|
is_rander = False
|
||||||
# 根据obj表渲染图片
|
# 根据obj表渲染图片
|
||||||
for obj_file in sorted_obj_file_list:
|
for obj_file in sorted_obj_file_list:
|
||||||
print(f"开始执行blender渲染--{obj_file}")
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
# 初始化blender环境
|
# 初始化blender环境
|
||||||
bpy.ops.wm.read_homefile()
|
bpy.ops.wm.read_homefile()
|
||||||
@@ -124,12 +122,12 @@ def rander_image_and_check(args):
|
|||||||
# 导入模型文件
|
# 导入模型文件
|
||||||
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: import pid: {pid}')
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: import pid: {pid}')
|
||||||
start_import = time.time()
|
start_import = time.time()
|
||||||
print("===============", os.path.join(args.input, obj_file))
|
print("===============", os.path.join(args.input, pid, obj_file))
|
||||||
with stdout_redirected(to=os.devnull):
|
with stdout_redirected(to=os.devnull):
|
||||||
bpy.ops.import_scene.obj(filepath=os.path.join(args.input,obj_file))
|
bpy.ops.import_scene.obj(filepath=os.path.join(args.input, pid, obj_file))
|
||||||
print(
|
print(
|
||||||
f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: import pid: {pid} done in {time.time() - start_import:.2f}s')
|
f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: import pid: {pid} done in {time.time() - start_import:.2f}s')
|
||||||
texture_path = os.path.join(args.input,texture_file)
|
texture_path = os.path.join(args.input, pid, texture_file)
|
||||||
materials = bpy.data.materials
|
materials = bpy.data.materials
|
||||||
for material in materials:
|
for material in materials:
|
||||||
# 确保材质使用节点树
|
# 确保材质使用节点树
|
||||||
@@ -145,7 +143,17 @@ def rander_image_and_check(args):
|
|||||||
imported_object = bpy.context.selected_objects[0] # 假设只导入一个模型
|
imported_object = bpy.context.selected_objects[0] # 假设只导入一个模型
|
||||||
# 获取模型的边界框(bounding box)
|
# 获取模型的边界框(bounding box)
|
||||||
bbox_corners = [imported_object.matrix_world @ Vector(corner) for corner in imported_object.bound_box]
|
bbox_corners = [imported_object.matrix_world @ Vector(corner) for corner in imported_object.bound_box]
|
||||||
|
min_z = min(corner.z for corner in bbox_corners)
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
imported_object.select_set(True)
|
||||||
|
bpy.context.view_layer.objects.active = imported_object
|
||||||
|
|
||||||
|
# Move object down so lowest point is at origin
|
||||||
|
imported_object.location.z -= min_z
|
||||||
|
|
||||||
|
# Apply transformations
|
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
|
||||||
|
bbox_corners = [imported_object.matrix_world @ Vector(corner) for corner in imported_object.bound_box]
|
||||||
# 获取最高点的 Y 值
|
# 获取最高点的 Y 值
|
||||||
model_height = max(corner.z for corner in bbox_corners)
|
model_height = max(corner.z for corner in bbox_corners)
|
||||||
|
|
||||||
@@ -223,8 +231,10 @@ def rander_image_and_check(args):
|
|||||||
os.makedirs(os.path.join(args.output, args.len_type,
|
os.makedirs(os.path.join(args.output, args.len_type,
|
||||||
f'{pid}_{args.resolution_x}x{args.resolution_y}_{args.ps_column}', camera_id),
|
f'{pid}_{args.resolution_x}x{args.resolution_y}_{args.ps_column}', camera_id),
|
||||||
exist_ok=True)
|
exist_ok=True)
|
||||||
png_name = f"{pid}_pic.png"
|
png_name = f"{pid}_{column + 1}.png"
|
||||||
png_out_path = os.path.join(args.output,png_name)
|
png_out_path = os.path.join(args.output, args.len_type,
|
||||||
|
f'{pid}_{args.resolution_x}x{args.resolution_y}_{args.ps_column}',
|
||||||
|
camera_id, png_name)
|
||||||
bpy.context.scene.render.filepath = png_out_path
|
bpy.context.scene.render.filepath = png_out_path
|
||||||
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
||||||
|
|
||||||
@@ -246,7 +256,7 @@ def rander_image_and_check(args):
|
|||||||
|
|
||||||
def get_args():
|
def get_args():
|
||||||
argparser = argparse.ArgumentParser(description='Render the camera in the studio')
|
argparser = argparse.ArgumentParser(description='Render the camera in the studio')
|
||||||
argparser.add_argument('-pid', '--pid', type=str, default='', help='pid of the model')
|
argparser.add_argument('-pid', '--pid', type=str, default='236479', help='pid of the model')
|
||||||
argparser.add_argument('-i', '--input', type=str, default='/data/datasets_20t/obj_rander/',
|
argparser.add_argument('-i', '--input', type=str, default='/data/datasets_20t/obj_rander/',
|
||||||
help='input models path')
|
help='input models path')
|
||||||
argparser.add_argument('-o', '--output', type=str, default='/data/datasets_20t/render_images/',
|
argparser.add_argument('-o', '--output', type=str, default='/data/datasets_20t/render_images/',
|
||||||
@@ -258,7 +268,7 @@ def get_args():
|
|||||||
argparser.add_argument('-r_x', '--resolution_x', type=int, default='768', help='render resolution_x')
|
argparser.add_argument('-r_x', '--resolution_x', type=int, default='768', help='render resolution_x')
|
||||||
argparser.add_argument('-r_y', '--resolution_y', type=int, default='1024', help='render resolution_y')
|
argparser.add_argument('-r_y', '--resolution_y', type=int, default='1024', help='render resolution_y')
|
||||||
argparser.add_argument('-s', '--sample', type=int, default='512', help='render sample')
|
argparser.add_argument('-s', '--sample', type=int, default='512', help='render sample')
|
||||||
argparser.add_argument('-e', '--Env_Texture_path', type=str, default='D://make2/tools/pic_for_obj/hdrs/studio_small_08_2k.exr',
|
argparser.add_argument('-e', '--Env_Texture_path', type=str, default='/data/datasets/hdrs/studio_small_08_2k.exr',
|
||||||
help='enviroment texture path')
|
help='enviroment texture path')
|
||||||
args = argparser.parse_args()
|
args = argparser.parse_args()
|
||||||
|
|
||||||
@@ -285,7 +295,6 @@ def rander_image_run(args):
|
|||||||
if not os.path.exists(args.output):
|
if not os.path.exists(args.output):
|
||||||
os.makedirs(args.output)
|
os.makedirs(args.output)
|
||||||
try:
|
try:
|
||||||
print("进入渲染")
|
|
||||||
rander_image_and_check(args)
|
rander_image_and_check(args)
|
||||||
except :
|
except :
|
||||||
print("图片渲染错误",args.pid)
|
print("图片渲染错误",args.pid)
|
||||||
|
|||||||
@@ -0,0 +1,302 @@
|
|||||||
|
import math, os, time, sys, bpy, argparse
|
||||||
|
from tqdm import tqdm
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from mathutils import Vector
|
||||||
|
from PIL import Image
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def stdout_redirected(to=os.devnull):
|
||||||
|
'''
|
||||||
|
import os
|
||||||
|
|
||||||
|
with stdout_redirected(to=filename):
|
||||||
|
print("from Python")
|
||||||
|
os.system("echo non-Python applications are also supported")
|
||||||
|
'''
|
||||||
|
fd = sys.stdout.fileno()
|
||||||
|
|
||||||
|
##### assert that Python and C stdio write using the same file descriptor
|
||||||
|
####assert libc.fileno(ctypes.c_void_p.in_dll(libc, "stdout")) == fd == 1
|
||||||
|
|
||||||
|
def _redirect_stdout(to):
|
||||||
|
sys.stdout.close() # + implicit flush()
|
||||||
|
os.dup2(to.fileno(), fd) # fd writes to 'to' file
|
||||||
|
sys.stdout = os.fdopen(fd, 'w') # Python writes to fd
|
||||||
|
|
||||||
|
with os.fdopen(os.dup(fd), 'w') as old_stdout:
|
||||||
|
with open(to, 'w') as file:
|
||||||
|
_redirect_stdout(to=file)
|
||||||
|
try:
|
||||||
|
yield # allow code to be run with the redirected stdout
|
||||||
|
finally:
|
||||||
|
_redirect_stdout(to=old_stdout) # restore stdout.
|
||||||
|
# buffering and flags such as
|
||||||
|
# CLOEXEC may be different
|
||||||
|
|
||||||
|
|
||||||
|
def check_image_rander(png_image_dir):
|
||||||
|
"""检查图片生成是否正常"""
|
||||||
|
image_list = os.listdir(png_image_dir)
|
||||||
|
if len(image_list)>0:
|
||||||
|
image_path = os.path.join(png_image_dir,image_list[0])
|
||||||
|
img = Image.open(image_path)
|
||||||
|
|
||||||
|
# 将图像转换为 RGB 模式
|
||||||
|
img = img.convert("RGB")
|
||||||
|
|
||||||
|
# 将图像数据转换为 numpy 数组
|
||||||
|
img_data = np.array(img)
|
||||||
|
|
||||||
|
# 设定颜色分段数
|
||||||
|
color_bins = 24
|
||||||
|
|
||||||
|
# 将颜色分成 24 种,计算颜色区间
|
||||||
|
# 对每个 RGB 值进行分段
|
||||||
|
def get_color_bin(value, bins):
|
||||||
|
return (value * bins) // 256
|
||||||
|
|
||||||
|
# 用于存储所有颜色的集合
|
||||||
|
color_set = set()
|
||||||
|
|
||||||
|
# 遍历图像每一个像素
|
||||||
|
for row in img_data:
|
||||||
|
for pixel in row:
|
||||||
|
# 将 RGB 每个通道的值分段
|
||||||
|
r, g, b = pixel
|
||||||
|
r_bin = get_color_bin(r, color_bins)
|
||||||
|
g_bin = get_color_bin(g, color_bins)
|
||||||
|
b_bin = get_color_bin(b, color_bins)
|
||||||
|
|
||||||
|
# 将 RGB 分段后的颜色组合成一个元组
|
||||||
|
color_set.add((r_bin, g_bin, b_bin))
|
||||||
|
|
||||||
|
# 输出不同的颜色数量
|
||||||
|
print(f"该图像中有 {len(color_set)} 种颜色。")
|
||||||
|
if len(color_set)>20:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def rander_image_and_check(args):
|
||||||
|
"""渲染图片"""
|
||||||
|
# 列出obj列表
|
||||||
|
pid = args.pid
|
||||||
|
pid_file_dir = os.path.join(args.input)
|
||||||
|
if not os.path.exists(pid_file_dir):
|
||||||
|
print(f"{pid_file_dir} 文件夹不存在")
|
||||||
|
return
|
||||||
|
obj_file_list = [aa for aa in os.listdir(pid_file_dir) if aa.endswith(".obj")]
|
||||||
|
if len(obj_file_list)==0:
|
||||||
|
print("没有obj文件",pid)
|
||||||
|
return
|
||||||
|
texture_path_list = [aa for aa in os.listdir(pid_file_dir) if aa.endswith(".jpg")]
|
||||||
|
if len(texture_path_list)==0:
|
||||||
|
print("没有贴图文件",pid)
|
||||||
|
return
|
||||||
|
texture_file = texture_path_list[0]
|
||||||
|
sorted_obj_file_list = sorted(obj_file_list, key=len)
|
||||||
|
print(sorted_obj_file_list)
|
||||||
|
is_rander = False
|
||||||
|
# 根据obj表渲染图片
|
||||||
|
for obj_file in sorted_obj_file_list:
|
||||||
|
print(f"开始执行blender渲染--{obj_file}")
|
||||||
|
start = time.time()
|
||||||
|
# 初始化blender环境
|
||||||
|
bpy.ops.wm.read_homefile()
|
||||||
|
bpy.ops.object.delete(use_global=False, confirm=False)
|
||||||
|
bpy.context.scene.unit_settings.scale_length = 1
|
||||||
|
bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS'
|
||||||
|
bpy.context.scene.unit_settings.mass_unit = 'GRAMS'
|
||||||
|
# bpy.context.scene.render.engine = 'CYCLES'
|
||||||
|
bpy.context.scene.render.engine = 'BLENDER_EEVEE' # ('BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'CYCLES')
|
||||||
|
bpy.context.scene.cycles.feature_set = 'SUPPORTED'
|
||||||
|
bpy.context.scene.cycles.device = 'GPU'
|
||||||
|
bpy.context.scene.cycles.preview_samples = args.sample
|
||||||
|
bpy.context.scene.cycles.samples = args.sample
|
||||||
|
bpy.context.scene.render.film_transparent = True
|
||||||
|
bpy.context.scene.cycles.use_progressive_refining = True # 启用渐进式渲染
|
||||||
|
|
||||||
|
# 导入模型文件
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: import pid: {pid}')
|
||||||
|
start_import = time.time()
|
||||||
|
print("===============", os.path.join(args.input, obj_file))
|
||||||
|
with stdout_redirected(to=os.devnull):
|
||||||
|
bpy.ops.import_scene.obj(filepath=os.path.join(args.input,obj_file))
|
||||||
|
print(
|
||||||
|
f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: import pid: {pid} done in {time.time() - start_import:.2f}s')
|
||||||
|
texture_path = os.path.join(args.input,texture_file)
|
||||||
|
materials = bpy.data.materials
|
||||||
|
for material in materials:
|
||||||
|
# 确保材质使用节点树
|
||||||
|
if not material.use_nodes:
|
||||||
|
material.use_nodes = True
|
||||||
|
node_tree = material.node_tree
|
||||||
|
texture_node = node_tree.nodes.new(type='ShaderNodeTexImage')
|
||||||
|
texture_node.image = bpy.data.images.load(texture_path)
|
||||||
|
bsdf_node = node_tree.nodes.get('Principled BSDF')
|
||||||
|
if bsdf_node:
|
||||||
|
node_tree.links.new(bsdf_node.inputs['Base Color'], texture_node.outputs['Color'])
|
||||||
|
# 获取模型对象
|
||||||
|
imported_object = bpy.context.selected_objects[0] # 假设只导入一个模型
|
||||||
|
# 获取模型的边界框(bounding box)
|
||||||
|
bbox_corners = [imported_object.matrix_world @ Vector(corner) for corner in imported_object.bound_box]
|
||||||
|
|
||||||
|
# 获取最高点的 Y 值
|
||||||
|
model_height = max(corner.z for corner in bbox_corners)
|
||||||
|
|
||||||
|
print(f"The highest Y value of the model is: {model_height}")
|
||||||
|
|
||||||
|
# 获取相机的位置 (假设相机位置参数已经在args中设定)
|
||||||
|
camera_position = args.cameras['1']['position']
|
||||||
|
|
||||||
|
# 计算缩放比例:假设模型的高度是1米(或者你可以通过其他方法计算模型的实际高度)
|
||||||
|
# model_height =25 # 这是一个假设值,实际应用中需要根据模型大小来设定
|
||||||
|
scale_factor = camera_position / model_height # 根据相机高度和模型高度计算缩放比例
|
||||||
|
bpy.context.view_layer.objects.active = imported_object
|
||||||
|
imported_object.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)
|
||||||
|
# 设置模型的缩放
|
||||||
|
imported_object.scale = (scale_factor, scale_factor, scale_factor)
|
||||||
|
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.data.scenes['Scene'].world.use_nodes = True
|
||||||
|
enode = bpy.data.scenes['Scene'].world.node_tree.nodes.new("ShaderNodeTexEnvironment")
|
||||||
|
enode.image = bpy.data.images.load(args.Env_Texture_path)
|
||||||
|
bpy.data.scenes['Scene'].world.node_tree.links.new(enode.outputs['Color'],
|
||||||
|
bpy.data.scenes['Scene'].world.node_tree.nodes[
|
||||||
|
'Background'].inputs['Color'])
|
||||||
|
# 初始化相机
|
||||||
|
camera_lens = 9 # 相机焦距,调整焦距改变镜头广角角度覆盖视野范围
|
||||||
|
camera_sensor_width = 25.4 # 相机传感器宽度 1英寸 = 25.4mm
|
||||||
|
camera_angle = 2 * math.pi / args.ps_column # 每台相机的圆心角(弧度)
|
||||||
|
# 初始相机参数
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
obj_camera = bpy.data.objects['Camera']
|
||||||
|
bpy.context.view_layer.objects.active = obj_camera
|
||||||
|
obj_camera.select_set(True)
|
||||||
|
bpy.context.object.data.type = args.len_type
|
||||||
|
bpy.context.object.data.sensor_width = camera_sensor_width
|
||||||
|
bpy.context.scene.render.resolution_x = args.resolution_x
|
||||||
|
bpy.context.scene.render.resolution_y = args.resolution_y
|
||||||
|
obj_camera.name = 'camera_init'
|
||||||
|
|
||||||
|
# 计算并输出所有相机的坐标和旋转
|
||||||
|
for column in tqdm(range(args.ps_column)):
|
||||||
|
theta = (column + 1) * camera_angle
|
||||||
|
x = args.ps_radius * math.sin(theta)
|
||||||
|
y = args.ps_radius * math.cos(theta)
|
||||||
|
|
||||||
|
for camera_id, camera_info in args.cameras.items():
|
||||||
|
x_ble = x
|
||||||
|
y_ble = y
|
||||||
|
z_ble = camera_info['position']
|
||||||
|
|
||||||
|
camera_copy_name = obj_camera.copy()
|
||||||
|
camera_copy_name.data = obj_camera.data.copy()
|
||||||
|
bpy.context.collection.objects.link(camera_copy_name)
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
bpy.context.view_layer.objects.active = camera_copy_name
|
||||||
|
camera_copy_name.select_set(True)
|
||||||
|
camera_copy_name.name = f"camera_{column + 1}_{camera_id}"
|
||||||
|
|
||||||
|
if args.len_type == 'PERSP':
|
||||||
|
bpy.context.object.data.lens = camera_info['PERSP_lens']
|
||||||
|
elif args.len_type == 'ORTHO':
|
||||||
|
bpy.context.object.data.ortho_scale = camera_info['ORTHO_scale']
|
||||||
|
|
||||||
|
camera_copy_name.location = (x_ble, -y_ble, z_ble)
|
||||||
|
camera_copy_name.rotation_euler = (camera_info['rotation'], 0, theta)
|
||||||
|
|
||||||
|
# 渲染图片
|
||||||
|
bpy.context.scene.camera = bpy.data.objects[camera_copy_name.name] # 切换当前相机
|
||||||
|
os.makedirs(os.path.join(args.output, args.len_type,
|
||||||
|
f'{pid}_{args.resolution_x}x{args.resolution_y}_{args.ps_column}', camera_id),
|
||||||
|
exist_ok=True)
|
||||||
|
png_name = f"{pid}_pic.png"
|
||||||
|
png_out_path = os.path.join(args.output,png_name)
|
||||||
|
bpy.context.scene.render.filepath = png_out_path
|
||||||
|
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
||||||
|
|
||||||
|
with stdout_redirected():
|
||||||
|
bpy.ops.render.render(write_still=True)
|
||||||
|
|
||||||
|
# delete camera_init
|
||||||
|
bpy.data.objects.remove(bpy.context.scene.objects['camera_init'])
|
||||||
|
bpy.data.objects.remove(bpy.context.scene.objects['Light'])
|
||||||
|
bpy.ops.wm.quit_blender()
|
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: render pid: {pid} done, time: {time.time() - start:.2f}s')
|
||||||
|
png_image_dir = os.path.join(args.output, args.len_type,f'{pid}_{args.resolution_x}x{args.resolution_y}_{args.ps_column}',"1",)
|
||||||
|
time.sleep(2)
|
||||||
|
if os.path.exists(png_image_dir):
|
||||||
|
if check_image_rander(png_image_dir):
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_args():
|
||||||
|
argparser = argparse.ArgumentParser(description='Render the camera in the studio')
|
||||||
|
argparser.add_argument('-pid', '--pid', type=str, default='', help='pid of the model')
|
||||||
|
argparser.add_argument('-i', '--input', type=str, default='/data/datasets_20t/obj_rander/',
|
||||||
|
help='input models path')
|
||||||
|
argparser.add_argument('-o', '--output', type=str, default='/data/datasets_20t/render_images/',
|
||||||
|
help='output images path')
|
||||||
|
argparser.add_argument('-l', '--limit', type=int, default=0, help='render limit')
|
||||||
|
argparser.add_argument('-ps_radius', '--ps_radius', type=float, default='18', help='photo studio radius, unit: m')
|
||||||
|
argparser.add_argument('-ps_column', '--ps_column', type=int, default='1', help='photo studio column number')
|
||||||
|
argparser.add_argument('-t', '--len_type', type=str, default='ORTHO', help='camera lens type, PERSP or.ORTHO')
|
||||||
|
argparser.add_argument('-r_x', '--resolution_x', type=int, default='768', help='render resolution_x')
|
||||||
|
argparser.add_argument('-r_y', '--resolution_y', type=int, default='1024', help='render resolution_y')
|
||||||
|
argparser.add_argument('-s', '--sample', type=int, default='512', help='render sample')
|
||||||
|
argparser.add_argument('-e', '--Env_Texture_path', type=str, default='D://make2/tools/pic_for_obj/hdrs/studio_small_08_2k.exr',
|
||||||
|
help='enviroment texture path')
|
||||||
|
args = argparser.parse_args()
|
||||||
|
|
||||||
|
# 相机参数,人体按2米身高,多人拍照覆盖设置
|
||||||
|
args.cameras = {
|
||||||
|
'1': {
|
||||||
|
'position': 2.5, # 1号相机的位置 0.9米高
|
||||||
|
'rotation': math.radians(86),
|
||||||
|
'PERSP_lens': 8,
|
||||||
|
'ORTHO_scale': 3.0,
|
||||||
|
},
|
||||||
|
# '2': {
|
||||||
|
# 'position': 1.8 , # 2号相机的位置 1.8米高
|
||||||
|
# 'rotation': math.radians(58),
|
||||||
|
# 'PERSP_lens': 13,
|
||||||
|
# 'ORTHO_scale': 2.5,
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def rander_image_run(args):
|
||||||
|
"""主流程"""
|
||||||
|
if not os.path.exists(args.output):
|
||||||
|
os.makedirs(args.output)
|
||||||
|
try:
|
||||||
|
print("进入渲染")
|
||||||
|
rander_image_and_check(args)
|
||||||
|
except :
|
||||||
|
print("图片渲染错误",args.pid)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = get_args()
|
||||||
|
rander_image_run(args)
|
||||||
|
#python image_rander_small.py -pid 234161 -i /data/datasets_20t/obj_rander/ -o /data/datasets_20t/render_images/
|
||||||
|
# python image_rander_small.py -pid 5924 -i /data/datasets_20t/obj_rander/ -o /data/datasets_20t/render_images/
|
||||||
|
"""
|
||||||
|
argparser.add_argument('-e', '--Env_Texture_path', type=str, default='/data/datasets/hdrs/studio_small_08_2k.exr',
|
||||||
|
替换成自己的hdrs路径 bpy 3.4.0
|
||||||
|
|
||||||
|
"""
|
||||||
Reference in new issue
Block a user