PYTHON: Coordinates of filled contour - python

I have a certain shape that I want the coordinates of the filled contour. I can easily get the coordinates of the contour easily with matplotlib cs = plt.contour(z) and cs.collections[0].get_paths()[0].vertices.T or cs.allsegs. But how do I fill that contour with 1s per example in z (a 2D array) once I have the contour's coordinates?
I have tried to get the coordinates of the filled contour with plt.contourf but still only get the coordinates of the contour.

Related

How to use the matrix from cv2.getPerspectiveTransform() to get a real distance from pixel coordinates

These days, I am trying to obtain 3D coordinates from a 2D coordinate on a BEV-transformed screen.
According to my algorithm, I have to multiply a particular pixel coordinate x, y by a BEV transform matrix, and multiply it by a matrix for 3d transform.
I got a 3x3 matrix from cv2.getPerspectiveTransform() but pixel coordinates are 2x1.
Is it correct to change (x, y) to (x, y, 1) From the perspective of normalizing the coordinates?
The matrix obtained from cv2.getPerspectiveTransform() is multiplied by pixel coordinates anyway to create a new pixel plane, why is it 3x3?

How can I associate rgb values to pixel locations in a perspective projection?

I am trying to associate rgb values to pixel coordinates after having done a perspective projection. The equation for the perspective projection is:
where x, y, are the pixel locations of the point, X, Y, and Z are locations of points in the camera frame, and the other parameters denote the intrinsic camera parameters. Given a point cloud containing the point locations and rgb values, I would like to associate rgb values to pixel locations according to the perspective projection.
The following code should create the correct image:
import matplotlib.pyplot as plt
import open3d as o3d
import numpy as np
cx = 325.5;
cy = 253.5;
fx = 518.0;
fy = 519.0;
K = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]])
pcd = o3d.io.read_point_cloud('freiburg.pcd', remove_nan_points=True)
points = np.array(pcd.points)
colors = np.array(pcd.colors)
projection = (K # points.T).T
normalization = projection / projection[:, [2]] #last elemet must be 1
pixel_coordinates = normalization.astype(int)
img = np.zeros((480, 640, 3))
#how can I fill the img appropriately? The matrix pixel coordinates should
# inform about where to place the color intensities.
for position, intensity in zip(pixel_coordinates, colors):
row, column = position[0], position[1]
#img[row, column, :] = intensity # returns with error
img[column, row, :] = intensity # gives a strange picture.
The point cloud can be read here. I expect to be able to associate the rgb values in the last loop:
for position, intensity in zip(pixel_coordinates, colors):
row, column = position[0], position[1]
#img[row, column, :] = intensity # returns with error
img[column, row, :] = intensity # gives a strange picture.
Strangely, if the second-to-last line is not commented, the program returns and IndexError while attempting to write a rgb values outside the range of available columns. The last line in the loop runs however without problems. The generated picture and the correct picture can be seen below:
How can I modify the code above to obtain the correct image?
A couple of issues:
You are ignoring the nonlinear distortion in the projection. Are the images you are comparing to undistorted? If they are, are you sure your projection matrix K is the one associated to the undistorted image?
Projecting the 3D points will inevitably produce a point cloud on the image plane, not a continuous image. To produce an image somewhat natural you likely need to interpolate nearby samples in the 2D point cloud. Your choice of interpolation filter determines the quality of the result. For example, you could first make an image of rgb buckets, a similar image of weights, project the 3d points, place their rgb values in the closest bucket (the one obtained by rounding the projection x,y coords), with a weight equal to the reciprocal of the distance of the projection from the bucket's center (i.e. the reciprocal of the euclidean norm of the rounding residuals). You then first compute the output pixel values as weighted averages at each bucket and then, if there are any unfilled bucket, you fill them by (say) bilinear interpolation of the filled neighbors. The last step will fill 1-pixel holes surrounded by already filled values. For larger holes you will need to choose some kind of infill procedure.

How to calculate the shape from lon/lat coordinates and draw it with plotly

I have a plotly based map where I am showing several coordinates in a Mapbox scatter plot. Now I want to get a shape from these coordinates and draw a shape on the map.
The coordinates are available as a pandas.Series. Below an extract of the coordinates.
0 [[51.795, 3.363], [51.79483333333334, 3.363], ...
1 [[51.42536, 2.622246666666667], [51.4256883333 ...
How can I get a shape for these coordinates which boundaries are the outmost coordinates of the cluster?
In geometry, there are basically two main concepts Convex hull and Alpha shape (also called Concave hull) to get the shape of a finite set of points. The following picture should visually describe the differences between them [2].
In case you want to have the convex hull you can use scipy.spatial.ConvexHull [4].
As alternatives to scipy you can also check geopandas and alphashape.

Is the centroid of a contour always its geometrical centre? (OpenCV, Python)

I am working with OpenCV+Python and I want to find the geometrical centre of the following contour:
The OpenCV documentation suggests the following to find the centroid of a contour:
import numpy as np
import cv2 as cv
img = cv.imread('star.jpg',0)
ret,thresh = cv.threshold(img,127,255,0)
im2,contours,hierarchy = cv.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv.moments(cnt)
print( M )
#Centroid
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
If I am right, according to this formula the centroid is calculated as the mean (or the average?) of all the points of the contour.
However, if for example fewer points are detected at the upper part of the contour than at the lower part of the contour then the centroid will be a bit higher than the actual geometrical centre of the (fully detected) contour.
Am I right?
If so, then is it better to calculate the average of the extreme points of the contour to find the geometrical centre of the contour and in this way to not depend at all on if the points of the contour are uniformly detected?
Am I right?
No. The OpenCV function moments() uses Green's theorem as mentioned in the OpenCV moments() docs. Green's theorem is indeed a correct way to find the center of mass of a shape. Green's theorem specifically relates integrals about some shape to integrals about the shape's border. As such, it doesn't at all matter how many points define the border or not.
I say center of mass specifically, because you can pass in a single-channel image array into moments() as well to compute the moments and not just a point array. However for an image array, if the array is just binary, then the center of mass is the centroid. And with an array of points (from your contours), there is no array to tell the pixel values inside, so the result is similarly still the centroid.

ReprojectImageTo3D corresponding pixel in image (Stereo Vision)

I have a disparity map.
Based on the disparity map, hovering on the 'left image' displays:
X and y of the image, So if i hover on the top-left most, it will display x:0, y:0
The next step is to display distance of the specific pixel,to make my life easy, I will try to do it with reprojectImageTo3D(disp, Q)
I got Q from stereoRectify
now, reprojectImageTo3D in python, returns an n by 3 matrix.
So I can see, it is a row of x y z coordinates. Wondering, how can I know which pixel are these coordinates correspond to?
This is a sample of the 3D points that I saved using numpy.savetxt
http://pastebin.com/wwDCYwjA
BTW: I'm doing everything in python, but GUI in Java, I don't have time to study GUI in python.
If you correctly calculate your disparity map, you should get (n1,n2,1) dimensional array, where n1,n2 - number of image's pixels by axes, 1 - number of chanels (single channel, which contain distance in pixels between correspondent pixels from left and right images). You should check that by typing disp.shape. After that you should pass your disparity map's ndarray to reprojectImageTo3D function and get ndarray, which has (n1,n2,3) shape (third dimension contains X,Y,Z coords of 3D point). You can check that by typing:
threeDImage = reprojectImageTo3D(disp, Q)
print threeDImage.shape
And finally, since you made your disparity map based on left image, each pixel, which has coords x,y on left image (or disparity map), corresponds to threeDImage[x][y] 3D point. Keep in mind, that row:0, column:0 is the top-left element of the matrix, based on opencv handling images:
0/0---column--->
|
|
row
|
|
v

Categories