Code Optimization - Need SPEED for Building Normal Map [3D & 2D Graphics]

Viewed 47

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:

Figure 1

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:

  1. Download all the mesh related 3 files -Download_Files
  2. Link those 3 files in the location correctly
  3. 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)
0 Answers
Related