Objective
I have a 3D face mesh triangulated. I have computed the midpoint of each triangle in the mesh, and I have the normal vector (n1, n2,n3) for each triangle. The objective is to create a 2D grid with [width x Height] resembling an image (with z assumed to be a constant). This is placed on top of the face mesh.
Refer to this image:
From these normal vectors, I then derive an RGB triplet for the resulting image.
My code is functionally correct, but it is very very slow. I cannot afford such huge time. I need help in optimizing my code in such a way that this would be computed faster and more efficient.
Things to replicate:
- Download all the mesh related 3 files -Download_Files
- Link those 3 files in the location correctly
- Run the code
The slow block is highlighted with the comment Need Optimization HERE - VERY SLOW.
import os
import math
import numpy as np
import cv2
import matplotlib.pyplot as plt
def data_cleaning(filename):
# Using readlines()
data_list = []
file1 = open(filename, 'r')
Lines = file1.readlines()
Data_dic = dict()
for line in Lines:
line = line.split("(")[-1].split(")")[0]
line = line.split(",")
no = int(line[0])
x = float(line[1])
y = float(line[2])
z = float(line[3])
Data_dic[no] = [x,y,z]
# df = df.append({'No': no, 'X': x, 'Y': y, 'Z': z}, ignore_index=False)
# print(no, x, y, z)
data_list.append([x,y,z])
return Data_dic, data_list
def distance_2points(P1, P2):
p1 = np.array(P1)
p2 = np.array(P2)
squared_dist = np.sum((p1 - p2) ** 2, axis=0)
dist = np.sqrt(squared_dist)
return dist
def Pixel_Grid(Width, Height):
# Grid Generation Position
TOP_LEFT_X = -0.9296
TOP_RIGHT_X = 1.053
TOP_LEFT_Y = -0.8783
BOTTOM_LEFT_Y = 1.311
x = np.linspace(TOP_LEFT_X, TOP_RIGHT_X, Width)
y = np.linspace(TOP_LEFT_Y, BOTTOM_LEFT_Y, Height)
XX, YY = np.meshgrid(x, y, sparse=True)
return XX, YY
def midpoint(x1, y1, x2, y2):
x = (x1 + x2) / 2
y = (y1 + y2) / 2
return x, y
# Provide Meshgrid Sparse Data
def mid_points(xx, yy, w, h):
Width = w
Height = h
xx = xx.reshape(Width, 1)
l_x = len(xx)
l_y = len(yy)
all_midpoints = []
for i in range(l_y):
for j in range(l_x):
if (j + 1 < Width):
current_x = xx[j]
next_x = xx[j + 1]
if (i + 1 < Height):
current_y = yy[i]
next_y = yy[i + 1]
x_value, y_value = midpoint(current_x, current_y, next_x, next_y)
x_value = x_value.tolist()[0]
y_value = y_value.tolist()[0]
mid_point_xy = [x_value, y_value]
all_midpoints.append(mid_point_xy)
return all_midpoints
def Normal_map_fn(Mapping_dic, Mid_Points, indexing, Width, Height, Channels):
Normal_map = np.zeros((Height, Width, Channels), np.float32)
# ---------------------------------Need Optimization HERE - VERY SLOW ----------------------
for e, i in enumerate(Mid_Points):
p_y, p_x = i
dis = []
vala = []
all = []
for k, v in Mapping_dic.items():
Mid_point_triangle = v[0]
normal = v[1]
M_x, M_y, M_z = Mid_point_triangle
P1 = [p_x, p_y]
P2 = [M_x, M_y]
dist = distance_2points(P1, P2)
dis.append(dist)
vala.append(k)
all.append([k, dist, normal])
Min_value = min(dis)
Index = np.argmin(dis)
Key_Value = vala[Index]
k, dist, normal = all[Index]
val1, val2 = indexing[e]
Normal_map[val1, val2] = (normal[0], normal[1], normal[2])
dis = []
vala = []
print("done !", e)
# ----------------------------------------------------------------------------------
fig, ax = plt.subplots()
cax = plt.imshow(Normal_map, cmap='gray')
# cbar = fig.colorbar(cax)
plt.show()
name0 = os.path.join("Normal_map.png")
cv2.imwrite(name0, Normal_map)
return Normal_map
# Task 0 - Cleaning the Mesh, Normal and Triangle data
Vertex_Data = '../3D_Info/Vertex_Data.txt'
Triangle_Index = '../3D_Info/Mesh_Data.txt'
Normal_Vector = '../3D_Info/Face_Normal.txt'
Vertex, v_list = data_cleaning(Vertex_Data)
Triangle, t_list = data_cleaning(Triangle_Index)
Normal, n_list = data_cleaning(Normal_Vector)
# Task 1 - Removing Negative Normals and Triangles
Camera_Vector = [0, 0, 1]
l = len(Normal)
Negative_dic = dict()
angle = []
for i in range(l):
# print(Normal[i])
uv = np.dot(Normal[i], Camera_Vector)
u_mag = np.sqrt(pow(Normal[i][0], 2) + pow(Normal[i][1], 2) + pow(Normal[i][2], 2))
v_mag = np.sqrt(pow(Camera_Vector[0], 2) + pow(Camera_Vector[1], 2) + pow(Camera_Vector[2], 2))
res = math.acos(uv/u_mag*v_mag) * (180.0 / math.pi)
if (uv <= 0):
Negative_dic[i] = Normal[i]
angle.append(res)
nl = len(Negative_dic.items())
# Task 2 - Delete all the negative normals
for k, v in Negative_dic.items():
del Triangle[k]
del Normal[k]
# Task 3 - Mapping
Mapping_dic = dict()
for k, v in Triangle.items():
one, two, three = v
point_m = Vertex[one]
point_n = Vertex[two]
point_o = Vertex[three]
mid_x = (point_m[0] + point_n[0] + point_o[0]) / 3
mid_y = (point_m[1] + point_n[1] + point_o[1]) / 3
mid_z = (point_m[2] + point_n[2] + point_o[2]) / 3
Mid_point_triangle = [mid_x, mid_y, mid_z]
# point_list.append(Mid_point_triangle)
Mapping_dic[k] = [Mid_point_triangle, Normal[k]]
# Task 4- Baking Normal Map
Width = 600
Height = 800
Channels = 3
indexing = dict()
count = 0
for j in range(Height):
for i in range(Width):
indexing[count] = [j, i]
count = count + 1
XX, YY = Pixel_Grid(Width, Height)
Mid_Points = mid_points(XX, YY, Width, Height)
Normal = Normal_map_fn(Mapping_dic, Mid_Points, indexing, Width, Height, Channels)
