Search code examples
pythonarraysnumpymatrixprojection

Rasterize list of 3d points into 2d canvas


I have a List of 3D Points, this list looks something like (but contains around 45k points)

ps = array([[0.77258706, 1.1149821 , 1.0494938 ],
       [0.77258706, 1.1149821 , 1.0494938 ],
       [0.77258706, 1.1149821 , 1.0494938 ],
       [1.3196146 , 1.2129393 , 0.96248001],
       [1.3197775 , 1.2117194 , 0.96213955],
       [1.3199375 , 1.2104989 , 0.96179962],
       [1.3200942 , 1.2092786 , 0.96145791],
       [1.3202487 , 1.2080572 , 0.96111906],
       [1.3204001 , 1.2068353 , 0.96078068]])

and I want to project these onto a 2D Canvas.

Due to the Pointcloud already being aligned correctly, I can just take the x and y coordinates directly from the points and use the z component to set the color (all the values get scaled to fit on the canvas).

canv = np.zeros(shape=[3840, 2160, 3])
for p in ps:
    x_pos = p[0] * scale_factor
    y_pos = p[1] * scale_factor
    val =   p[2] * color_scale_factor
    canv[int(x_pos)][int(y_pos)][0:3] = colors[int(val)].get_rgb()

Currently this takes ca. 120ms per frame (8FPS).

Is there a way to significantly improve this by using some numpy magic? Any help is appreciated.


Solution

  • Use numpy functions instead of looping over the points:

    x_pos = (p[:, 0] * scale_factor).astype(int)
    y_pos = (p[:, 1] * scale_factor).astype(int)
    val =   (p[:, 2] * color_scale_factor).astype(int)
    

    Since colors seems like a list of some kind of Color object, you can pre-calculate an array of RGB values prior to doing anything:

    colors_rgb = np.array([c.get_rgb() for c in colors])
    

    Then, you can index into this array using your val:

    canv[x_pos, y_pos] = colors_rgb[val]
    

    To demonstrate this, I'm going to use an example list of points and a smaller canvas:

    canv = np.zeros((4, 4, 3))
    p = np.array([[0.1, 0.1, 0],
                  [1.1, 1.1, 1],
                  [2.2, 2.2, 2],
                  [3.3, 3.3, 3]])
    
    scale_factor = color_scale_factor = 1
    

    Assuming colors contains a bunch of Color objects (it doesn't matter what it contain as long as Color.get_rgb gives a (at least) 3-long iterable):

    class Color:
        def __init__(self, r, g, b):
            self.rgb = np.array([r, g, b]) / 255
    
        def get_rgb(self):
            return self.rgb
    
    colors = [Color(255, 0, 0), Color(0, 255, 0), Color(0, 0, 255), Color(128, 128, 0), Color(0, 128, 128), Color(128, 0, 128)]
    

    We have colors_rgb as:

    array([[1.        , 0.        , 0.        ],
           [0.        , 1.        , 0.        ],
           [0.        , 0.        , 1.        ],
           [0.50196078, 0.50196078, 0.        ],
           [0.        , 0.50196078, 0.50196078],
           [0.50196078, 0.        , 0.50196078]])
    

    and canv becomes:

    array([[[1.        , 0.        , 0.        ],
            [0.        , 0.        , 0.        ],
            [0.        , 0.        , 0.        ],
            [0.        , 0.        , 0.        ]],
    
           [[0.        , 0.        , 0.        ],
            [0.        , 1.        , 0.        ],
            [0.        , 0.        , 0.        ],
            [0.        , 0.        , 0.        ]],
    
           [[0.        , 0.        , 0.        ],
            [0.        , 0.        , 0.        ],
            [0.        , 0.        , 1.        ],
            [0.        , 0.        , 0.        ]],
    
           [[0.        , 0.        , 0.        ],
            [0.        , 0.        , 0.        ],
            [0.        , 0.        , 0.        ],
            [0.50196078, 0.50196078, 0.        ]]])