2 years ago

#55868

test-img

Dar

Create 3D points cloud from 2D images python

I have a dataset of 2d RGB images and I want to generate a 3D points cloud as a PLY file or CSV file in the following format x,y,z,r,g,b.

Should I first convert the 2D images to depth maps?

I have tried to convert one image to a depth map and I want to use the following function to create the 3d points cloud but the problem is that I don't know which values I can put in fx,fy, cx, cy, please someone have an idea can help me in that.

import numpy as np
from PIL import Image
import imageio
import Openexr
import struct
import os

def get_pointcloud(color_image,depth_image,camera_intrinsics):
    """ creates 3D point cloud of rgb images by taking depth information
        input : color image: numpy array[h,w,c], dtype= uint8
                depth image: numpy array[h,w] values of all channels will be same
        output : camera_points, color_points - both of shape(no. of pixels, 3)
    """

    image_height = depth_image.shape[0]
    image_width = depth_image.shape[1]
    pixel_x,pixel_y = np.meshgrid(np.linspace(0,image_width-1,image_width),
                                  np.linspace(0,image_height-1,image_height))
    camera_points_x = np.multiply(pixel_x-camera_intrinsics[0,2],depth_image/camera_intrinsics[0,0])
    camera_points_y = np.multiply(pixel_y-camera_intrinsics[1,2],depth_image/camera_intrinsics[1,1])
    camera_points_z = depth_image
    camera_points = np.array([camera_points_x,camera_points_y,camera_points_z]).transpose(1,2,0).reshape(-1,3)

    color_points = color_image.reshape(-1,3)

    # Remove invalid 3D points (where depth == 0)
    valid_depth_ind = np.where(depth_image.flatten() > 0)[0]
    camera_points = camera_points[valid_depth_ind,:]
    color_points = color_points[valid_depth_ind,:]

    return camera_points,color_points

def write_pointcloud(filename,xyz_points,rgb_points=None):

    """ creates a .pkl file of the point clouds generated
    """

    assert xyz_points.shape[1] == 3,'Input XYZ points should be Nx3 float array'
    if rgb_points is None:
        rgb_points = np.ones(xyz_points.shape).astype(np.uint8)*255
    assert xyz_points.shape == rgb_points.shape,'Input RGB colors should be Nx3 float array and have same size as input XYZ points'

    # Write header of .ply file
    fid = open(filename,'wb')
    fid.write(bytes('ply\n', 'utf-8'))
    fid.write(bytes('format binary_little_endian 1.0\n', 'utf-8'))
    fid.write(bytes('element vertex %d\n'%xyz_points.shape[0], 'utf-8'))
    fid.write(bytes('property float x\n', 'utf-8'))
    fid.write(bytes('property float y\n', 'utf-8'))
    fid.write(bytes('property float z\n', 'utf-8'))
    fid.write(bytes('property uchar red\n', 'utf-8'))
    fid.write(bytes('property uchar green\n', 'utf-8'))
    fid.write(bytes('property uchar blue\n', 'utf-8'))
    fid.write(bytes('end_header\n', 'utf-8'))

    # Write 3D points to .ply file
    for i in range(xyz_points.shape[0]):
        fid.write(bytearray(struct.pack("fffccc",xyz_points[i,0],xyz_points[i,1],xyz_points[i,2],
                                        rgb_points[i,0].tostring(),rgb_points[i,1].tostring(),
                                        rgb_points[i,2].tostring())))
    fid.close()

if __name__ == '__main__':
    import argparse

    # Parse command line arguments
    parser = argparse.ArgumentParser(
        description='create point cloud from depth and rgb image.')
    parser.add_argument('--rgb_filename', required=True,
                        help='path to the rgb image')
    parser.add_argument('--depth_filename', required=True,
                        help="path to the depth image ")
    parser.add_argument('--output_directory', required=True,
                        help="directory to save the point cloud file")
    parser.add_argument('--fx', required=True, type=float,
                        help="focal length along x-axis (longer side) in pixels")
    parser.add_argument('--fy', required=True, type=float,
                        help="focal length along y-axis (shorter side) in pixels")
    parser.add_argument('--cx', required=True, type=float,
                        help="centre of image along x-axis")
    parser.add_argument('--cy', required=True, type=float,
                        help="centre of image along y-axis")

    args = parser.parse_args()

    color_data = imageio.imread(args.rgb_filename)
    # color_data = np.asarray(im_color, dtype = "uint8")

    if os.path.splitext(os.path.basename(args.depth_filename))[1] == '.npy':
        depth_data = np.load(args.depth_filename)
    else:
        im_depth = imageio.imread(args.depth_filename)
        depth_data = im_depth[:,:,0] # values of all channels are equal


    # camera_intrinsics  = [[fx 0 cx],
    #                       [0 fy cy],
    #                       [0 0 1]]
    camera_intrinsics  = np.asarray([[args.fx, 0, args.cx], [0, args.fy, args.cy], [0, 0, 1]])

    filename = os.path.basename(args.rgb_filename)[:9] + '-pointCloud.ply'
    output_filename = os.path.join(args.output_directory, filename)

    print("Creating the point Cloud file at : ", output_filename )
    camera_points, color_points = get_pointcloud(color_data, depth_data, camera_intrinsics)

    write_pointcloud(output_filename, camera_points, color_points)

python

csv

2d

depth

ply-file-format

0 Answers

Your Answer

Accepted video resources