blender中获取虚拟相机渲染图片上每像素对应的纹理上的像素值

2024-09-03 19:52

本文主要是介绍blender中获取虚拟相机渲染图片上每像素对应的纹理上的像素值,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

示例图:

相机渲染出图后,图片上每个像素点中对应的纹理的像素值。获取这个对应关系存到数据库

基本思路是

从相机圆心发射射线接触到物体时获取接触点(三维坐标)所在三角面,通过这个三角面的三个顶点坐标及其三个纹理坐标,通过重心坐标求出接触点所对应的纹理坐标。在发射射线时,通过相机分辨率中某一点的二维坐标(即渲染出图后图片上的坐标)转三维坐标,结合相机圆心确认射线方向。至此【图片上每个像素点中对应的纹理的像素值】对应关系所需要的数据已具备,

示例代码

如上图,相机视野中存在一个半球模型(已贴图),一个模型船

import bpy
import mathutils
import sqlite3
import time
import numpy as np
from PIL import Image# 虚拟相机名称的变量,用与获取自定义命名方式的物体。
name = 'bow'
# 导出数据库位置
dbpath = 'D:/syncdisk/blender_export/' + name + '.db'
# 渲染出图的位置
export_image_path = 'D:/syncdisk/blender_export/' + name + '.png'
# 获取当前场景中的相机对象
camera = bpy.data.objects[name + ".001"]
# 获取对象的网格数据
back_mesh = bpy.data.objects["球体.001"]
ship = bpy.data.objects["Box5885.001"]
line = bpy.data.objects["Box5885.003"]# 链接数据库的句柄
db = sqlite3.connect(dbpath)
# cursor对象
db_cur = db.cursor()# 获取当前渲染设置
render_settings = bpy.context.scene.render
# 获取渲染图的尺寸
render_width = render_settings.resolution_x
render_height = render_settings.resolution_y
# 获取所有材质,用于获取材质颜色
materials = bpy.data.materialsmesh = back_mesh.data
# 获取模型的世界变换矩阵
world_mat = back_mesh.matrix_world
# 获取uv层,用于遍历获取uv数据
uv_layers_data = back_mesh.data.uv_layers.active.data
# 存储要写入数据库的数据
sql_value = []# 清空表格
def clearTable(tableName, cur):print('开始清空表'+tableName)sql = 'delete from ' + tableName + ' where 1 = 1'try:cur.execute(sql)print('清空表' + tableName + '成功')except Exception as e:print(e)print('清空表' + tableName + '失败')def dissconnectDB(cur):# 关闭游标cur.close()# 关闭连接db.close()print('断开数据库链接')# 执行sql创建表
def createTable(cur):print('开始创建表:pixelmap')# 执行sql创建表sql = 'create table pixelmap(id integer primary key,canvas integer not null,row0 float not null,col0 float not null,source  integer not null,row1 float not null,col1 float not null,wt float not null)'try:cur.execute(sql)print('创建表:pixelmap成功')except Exception as e:print(e)print('创建表:pixelmap失败')print('开始创建表:keyvalue')sql = 'create table keyvalue(id integer primary key,key string,value string)'try:cur.execute(sql)print('创建表:keyvalue成功')except Exception as e:print(e)print('创建表:keyvalue失败')def insertValueIntoTable(value, cur):print('开始插入数据到表pixelmap')try:# 执行sql创建表sql = 'insert into pixelmap(canvas,row0,col0,source,row1,col1,wt) values(?,?,?,?,?,?,?)'cur.executemany(sql, value)# 提交事务db.commit()print('插入成功')except Exception as e:print('插入失败')print(e)db.rollback()def insertValueIntoKeyValueTable(value, cur):print('开始插入数据到表keyvalue')try:# 执行sql创建表sql = 'insert into keyvalue(key,value) values(?,?)'cur.executemany(sql, value)# 提交事务db.commit()print('插入成功')except Exception as e:print('插入失败')print(e)db.rollback()def coord2_3d(camera, coord):out = mathutils.Vector(((2.0 * coord[0] / render_width) - 1.0,(2.0 * (1.0 - coord[1] / render_height)) - 1.0,-0.5))# 获取相机的投影矩阵perspective_matrix = camera.calc_matrix_camera(bpy.context.evaluated_depsgraph_get(),x = render_width,y = render_height)persinv = perspective_matrix.inverted()coord_world = camera.matrix_world @ (persinv @ out)return coord_worlddef obj_ray_cast(obj, coord, camera):view_vector = coord2_3d(camera, coord)ray_origin = camera.matrix_world.translationmatrix_inv = obj.matrix_world.copy().inverted()ray_origin_obj = matrix_inv @ ray_originray_target_obj = matrix_inv @ view_vectorray_direction_obj = ray_target_obj - ray_origin_obj# cast the raysuccess, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj)if success:coord_world = back_mesh.matrix_world @ locationreturn (coord_world, face_index)else:return ((-1, -1), -1)# 计算三角形的面积
def triangle_area(v1, v2, v3):AB = v2 - v1AC = v3 - v1area = 0.5 * np.linalg.norm(np.cross(AB, AC))return areadef calculate_barycenter_3d(point, points):A = triangle_area(points[0], points[1], points[2])A1 = triangle_area(point, points[1], points[2])A2 = triangle_area(points[0], point, points[2])A3 = triangle_area(points[0], points[1], point)return (A1 / A, A2 / A, A3 / A, (A1 / A + A2 / A + A3 / A))def calculate_point_from_barycenter(barycenter, points):if len(barycenter) != 3 or len(points) != 3:return Nonex = barycenter[0] * points[0][0] + barycenter[1] * points[1][0] + barycenter[2] * points[2][0]y = barycenter[0] * points[0][1] + barycenter[1] * points[1][1] + barycenter[2] * points[2][1]return x, y# 计算点在三角形中的重心坐标,返回的坐标在值都是大于0的,则在三角形内部
def calculate_barycenter(point, points):if len(point) != 2:return (-1, 1, 0)if len(points) != 3:return (-1, 1, 0)x, y = point[0], point[1]p1, p2, p3 = points[0], points[1], points[2]denominator = (p2[1] - p3[1]) * (p1[0] - p3[0]) + (p3[0] - p2[0]) * (p1[1] - p3[1])if denominator == 0.0:return (-1, -1, -1)alpha = ((p2[1] - p3[1]) * (x - p3[0]) + (p3[0] - p2[0]) * (y - p3[1])) / denominatorbeta = ((p3[1] - p1[1]) * (x - p3[0]) + (p1[0] - p3[0]) * (y - p3[1])) / denominatorgamma = 1.0 - alpha - betareturn (alpha, beta, gamma)def rgba_to_hex(rgb):red = round(rgb[0] * 255)green = round(rgb[1] * 255)blue = round(rgb[2] * 255)hex_color = (red << 16) + (green << 8) + bluereturn hex(hex_color)# 获取材质的颜色(纯色材质)
def getMaterialColor(index):# 获取所有的材质materials = bpy.data.materialsmaterial = materials[index]# 如果材质包含 Principled BSDF Shader,则获取 Base Colorif material.use_nodes:nodes = material.node_tree.nodesprincipled_bsdf = nodes.get("Principled BSDF")if principled_bsdf is not None:base_color = principled_bsdf.inputs["Base Color"].default_valuereturn rgba_to_hex((base_color[0], base_color[1], base_color[2]))# print(f"Material Index: {index}, Base Color: {base_color[0],base_color[1],base_color[2],base_color[3]}")# 否则,尝试获取 Diffuse Shader 的颜色else:diffuse_shader = material.diffuse_colorreturn rgba_to_hex(diffuse_shader)def open_image(filepath):return Image.open(filepath)def get_pixel_color(image, x, y):# 获取指定位置的像素颜色值color = image.getpixel((x, y))red = color[0]green = color[1]blue = color[2]hex_color = (red << 16) + (green << 8) + bluereturn hex(hex_color)try:createTable(db_cur)clearTable('pixelmap',db_cur)clearTable('keyvalue',db_cur)# 渲染图像print("开始渲染图片")bpy.context.scene.render.filepath = export_image_path  # 输出路径bpy.context.scene.render.resolution_x = render_width  # 分辨率Xbpy.context.scene.render.resolution_y = render_height  # 分辨率Ybpy.ops.render.render(write_still=True)print("渲染图片结束")time.sleep(1)# 获取渲染图句柄print("获取渲染图句柄")image = open_image(export_image_path)for y in range(render_height):print("loading y...", y)for x in range(render_width):# 获取面的所有顶点对应的纹理坐标tex_coords = []# 获取面的所有顶点vertices = []coord = x, yship_coord, face_index_ship = obj_ray_cast(ship, coord, camera)# line_coord, face_index_line = obj_ray_cast(line, coord, camera)if ship_coord[0] != -1:sql_value.append((0, y, x, -1, 0.0, 0.0, get_pixel_color(image, x, y)))# elif line_coord[0] != -1:#     sql_value.append((0, y, x, -1, 0.0, 0.0, get_pixel_color(image, x, y)))else:coord_world, face_index = obj_ray_cast(back_mesh, coord, camera)if coord_world[0] != -1:face = mesh.polygons[face_index]loop_start = face.loop_startloop_end = face.loop_start + face.loop_totalif face.loop_total == 4:# blender中是使用四角面,也就是两个三角面合并后的面计算# 先获取到四个顶点vertices_0 = mesh.loops[face.loop_start].vertex_indexvertices_0_w = world_mat @ mesh.vertices[vertices_0].cotex_coords.append(uv_layers_data[face.loop_start].uv)vertices.append(vertices_0_w)vertices_1 = mesh.loops[face.loop_start+1].vertex_indexvertices_1_w = world_mat @ mesh.vertices[vertices_1].cotex_coords.append(uv_layers_data[face.loop_start+1].uv)vertices.append(vertices_1_w)vertices_2 = mesh.loops[face.loop_start+2].vertex_indexvertices_2_w = world_mat @ mesh.vertices[vertices_2].cotex_coords.append(uv_layers_data[face.loop_start+2].uv)vertices.append(vertices_2_w)# 计算重心坐标,判断该像素点是否在第一个三角面内barycenter = calculate_barycenter_3d(coord_world, vertices) if (barycenter[3] <= 1.000001):coord_xy = calculate_point_from_barycenter((barycenter[0], barycenter[1], barycenter[2]), tex_coords)sql_value.append((0, y, x, float(face.material_index), 1-coord_xy[1], coord_xy[0], 1.0))# 第二个三角面else:vertices_3 = mesh.loops[face.loop_start+3].vertex_indexvertices_3_w = world_mat @ mesh.vertices[vertices_3].cotex_coords[1] = uv_layers_data[face.loop_start+2].uvtex_coords[2] = uv_layers_data[face.loop_start+3].uvvertices[1] = vertices_2_wvertices[2] = vertices_3_w# 计算重心坐标,判断该像素点是否在第一个三角面内barycenter = calculate_barycenter_3d(coord_world, vertices)if (barycenter[3] <= 1.000001):coord_xy = calculate_point_from_barycenter((barycenter[0], barycenter[1], barycenter[2]), tex_coords)sql_value.append((0, y, x, float(face.material_index), 1-coord_xy[1], coord_xy[0], 1.0))else:vertices_0 = mesh.loops[face.loop_start].vertex_indextex_coords.append(uv_layers_data[face.loop_start].uv)vertices.append(world_mat @ mesh.vertices[vertices_0].co)vertices_1 = mesh.loops[face.loop_start+1].vertex_indextex_coords.append(uv_layers_data[face.loop_start+1].uv)vertices.append(world_mat @ mesh.vertices[vertices_1].co)vertices_2 = mesh.loops[face.loop_start+2].vertex_indextex_coords.append(uv_layers_data[face.loop_start+2].uv)vertices.append(world_mat @ mesh.vertices[vertices_2].co)# 计算重心坐标,判断该像素点是否在第一个三角面内barycenter = calculate_barycenter_3d(coord_world, vertices)if (barycenter[3] <= 1.000001):coord_xy = calculate_point_from_barycenter((barycenter[0], barycenter[1], barycenter[2]), tex_coords)sql_value.append((0, y, x, float(face.material_index), 1-coord_xy[1], coord_xy[0], 1.0))# sql_value.append((0,render_width-x,y, float(face.material_index), 1-coord_xy[1], coord_xy[0], 1.0))insertValueIntoTable(sql_value, db_cur)insertValueIntoKeyValueTable([("canvas_width",render_width),("canvas_height",render_height)],db_cur)dissconnectDB(db_cur)except Exception as e:dissconnectDB(db_cur)

这篇关于blender中获取虚拟相机渲染图片上每像素对应的纹理上的像素值的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1133885

相关文章

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的

SpringMVC获取请求参数的方法

《SpringMVC获取请求参数的方法》:本文主要介绍SpringMVC获取请求参数的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下... 目录1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数3、@RequestParam4、@

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

使用Python实现全能手机虚拟键盘的示例代码

《使用Python实现全能手机虚拟键盘的示例代码》在数字化办公时代,你是否遇到过这样的场景:会议室投影电脑突然键盘失灵、躺在沙发上想远程控制书房电脑、或者需要给长辈远程协助操作?今天我要分享的Pyth... 目录一、项目概述:不止于键盘的远程控制方案1.1 创新价值1.2 技术栈全景二、需求实现步骤一、需求

基于Python实现高效PPT转图片工具

《基于Python实现高效PPT转图片工具》在日常工作中,PPT是我们常用的演示工具,但有时候我们需要将PPT的内容提取为图片格式以便于展示或保存,所以本文将用Python实现PPT转PNG工具,希望... 目录1. 概述2. 功能使用2.1 安装依赖2.2 使用步骤2.3 代码实现2.4 GUI界面3.效

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法

《golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法》:本文主要介绍golang获取当前时间、时间戳和时间字符串及它们之间的相互转换,本文通过实例代码给大家介绍的非常详细,感兴趣... 目录1、获取当前时间2、获取当前时间戳3、获取当前时间的字符串格式4、它们之间的相互转化上篇文章给大家介

Python实现AVIF图片与其他图片格式间的批量转换

《Python实现AVIF图片与其他图片格式间的批量转换》这篇文章主要为大家详细介绍了如何使用Pillow库实现AVIF与其他格式的相互转换,即将AVIF转换为常见的格式,比如JPG或PNG,需要的小... 目录环境配置1.将单个 AVIF 图片转换为 JPG 和 PNG2.批量转换目录下所有 AVIF 图

详解如何通过Python批量转换图片为PDF

《详解如何通过Python批量转换图片为PDF》:本文主要介绍如何基于Python+Tkinter开发的图片批量转PDF工具,可以支持批量添加图片,拖拽等操作,感兴趣的小伙伴可以参考一下... 目录1. 概述2. 功能亮点2.1 主要功能2.2 界面设计3. 使用指南3.1 运行环境3.2 使用步骤4. 核

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比