Find mean position of blackpixels using python - python-2.7

I have a binary image and I need to find the mean values of x and y of the black region. These values are calculated for a set of binary images and their mean values of x and y are plotted I don't know how to find this region and calculate their mean values of x and y. Any help is kindly appreciated.

If black pixels are not registered in some data structure, just calculate center of mass for black pixels:
sx = 0
sy = 0
black_cnt = 0
for y in y-range
for x in x-range
if black(x,y)
sx = sx + x
sy = sy + y
black_cnt++
sx = sx / black_cnt
sy = sy / black_cnt

You can obtain the mean positions using the moments of contours.
In order to find the mean you must calculate the first order moments of the contour.
CODE:
#---Read image and obtain threshold---
im = cv2.imread('1.jpg', 1)
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img, 120, 255, 1)
#---Obtain contours---
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnts = contours
cv2.drawContours(im, contours, -1, (0, 255, 0), 1)
#---Compute the center/mean of the contours---
for c in cnts:
M = cv2.moments(c)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
print cX
print cY
Values cX and cY have the mean positions of the contours.

Related

How to fit a 2D ellipse to given points

I would like to fit a 2D array by an elliptic function: (x / a)² + (y / b)² = 1 ----> (and so get the a and b)
And then, be able to replot it on my graph.
I found many examples on internet, but no one with this simple Cartesian equation. I probably have searched badly ! I think a basic solution for this problem could help many people.
Here is an example of the data:
Sadly, I can not put the values... So let's assume that I have an X,Y arrays defining the coordinates of each of those points.
This can be solved directly using least squares. You can frame this as minimizing the sum of squares of quantity (alpha * x_i^2 + beta * y_i^2 - 1) where alpha is 1/a^2 and beta is 1/b^2. You have all the x_i's in X and the y_i's in Y so you can find the minimizer of ||Ax - b||^2 where A is an Nx2 matrix (i.e. [X^2, Y^2]), x is the column vector [alpha; beta] and b is column vector of all ones.
The following code solves the more general problem for an ellipse of the form Ax^2 + Bxy + Cy^2 + Dx +Ey = 1 though the idea is exactly the same. The print statement gives 0.0776x^2 + 0.0315xy+0.125y^2+0.00457x+0.00314y = 1 and the image of the ellipse generated is also below
import numpy as np
import matplotlib.pyplot as plt
alpha = 5
beta = 3
N = 500
DIM = 2
np.random.seed(2)
# Generate random points on the unit circle by sampling uniform angles
theta = np.random.uniform(0, 2*np.pi, (N,1))
eps_noise = 0.2 * np.random.normal(size=[N,1])
circle = np.hstack([np.cos(theta), np.sin(theta)])
# Stretch and rotate circle to an ellipse with random linear tranformation
B = np.random.randint(-3, 3, (DIM, DIM))
noisy_ellipse = circle.dot(B) + eps_noise
# Extract x coords and y coords of the ellipse as column vectors
X = noisy_ellipse[:,0:1]
Y = noisy_ellipse[:,1:]
# Formulate and solve the least squares problem ||Ax - b ||^2
A = np.hstack([X**2, X * Y, Y**2, X, Y])
b = np.ones_like(X)
x = np.linalg.lstsq(A, b)[0].squeeze()
# Print the equation of the ellipse in standard form
print('The ellipse is given by {0:.3}x^2 + {1:.3}xy+{2:.3}y^2+{3:.3}x+{4:.3}y = 1'.format(x[0], x[1],x[2],x[3],x[4]))
# Plot the noisy data
plt.scatter(X, Y, label='Data Points')
# Plot the original ellipse from which the data was generated
phi = np.linspace(0, 2*np.pi, 1000).reshape((1000,1))
c = np.hstack([np.cos(phi), np.sin(phi)])
ground_truth_ellipse = c.dot(B)
plt.plot(ground_truth_ellipse[:,0], ground_truth_ellipse[:,1], 'k--', label='Generating Ellipse')
# Plot the least squares ellipse
x_coord = np.linspace(-5,5,300)
y_coord = np.linspace(-5,5,300)
X_coord, Y_coord = np.meshgrid(x_coord, y_coord)
Z_coord = x[0] * X_coord ** 2 + x[1] * X_coord * Y_coord + x[2] * Y_coord**2 + x[3] * X_coord + x[4] * Y_coord
plt.contour(X_coord, Y_coord, Z_coord, levels=[1], colors=('r'), linewidths=2)
plt.legend()
plt.xlabel('X')
plt.ylabel('Y')
plt.show()
Following the suggestion by ErroriSalvo, here is the complete process of fitting an ellipse using the SVD. The arrays x, y are coordinates of the given points, let's say there are N points. Then U, S, V are obtained from the SVD of the centered coordinate array of shape (2, N). So, U is a 2 by 2 orthogonal matrix (rotation), S is a vector of length 2 (singular values), and V, which we do not need, is an N by N orthogonal matrix.
The linear map transforming the unit circle to the ellipse of best fit is
sqrt(2/N) * U * diag(S)
where diag(S) is the diagonal matrix with singular values on the diagonal. To see why the factor of sqrt(2/N) is needed, imagine that the points x, y are taken uniformly from the unit circle. Then sum(x**2) + sum(y**2) is N, and so the coordinate matrix consists of two orthogonal rows of length sqrt(N/2), hence its norm (the largest singular value) is sqrt(N/2). We need to bring this down to 1 to have the unit circle.
N = 300
t = np.linspace(0, 2*np.pi, N)
x = 5*np.cos(t) + 0.2*np.random.normal(size=N) + 1
y = 4*np.sin(t+0.5) + 0.2*np.random.normal(size=N)
plt.plot(x, y, '.') # given points
xmean, ymean = x.mean(), y.mean()
x -= xmean
y -= ymean
U, S, V = np.linalg.svd(np.stack((x, y)))
tt = np.linspace(0, 2*np.pi, 1000)
circle = np.stack((np.cos(tt), np.sin(tt))) # unit circle
transform = np.sqrt(2/N) * U.dot(np.diag(S)) # transformation matrix
fit = transform.dot(circle) + np.array([[xmean], [ymean]])
plt.plot(fit[0, :], fit[1, :], 'r')
plt.show()
But if you assume that there is no rotation, then np.sqrt(2/N) * S is all you need; these are a and b in the equation of the ellipse.
You could try a Singular Value Decomposition of the data matrix.
https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.linalg.svd.html
First center the data by subtracting mean values of X,Y from each column respectively.
X=X-np.mean(X)
Y=Y-np.mean(Y)
D=np.vstack(X,Y)
Then, apply SVD and extract
-eigenvalues (members of s) -> axis length
-eigenvectors(U) -> axis orientation
U, s, V = np.linalg.svd(D, full_matrices=True)
This should be a least-squares fit.
Of course, things can get more complicated than this, please see
https://www.emis.de/journals/BBMS/Bulletin/sup962/gander.pdf

Transform images with bezier curves

I'm using this article: nonlingr as a font to understand non linear transformations, in the section GLYPHS ALONG A PATH he explains how to use a parametric curve to transform an image, i'm trying to apply a cubic bezier to an image, however i have been unsuccessfull, this is my code:
OUT.aloc(IN.width(), IN.height());
//get the control points...
wVector p0(values[vindex], values[vindex+1], 1);
wVector p1(values[vindex+2], values[vindex+3], 1);
wVector p2(values[vindex+4], values[vindex+5], 1);
wVector p3(values[vindex+6], values[vindex+7], 1);
//this is to calculate t based on x
double trange = 1 / (OUT.width()-1);
//curve coefficients
double A = (-p0[0] + 3*p1[0] - 3*p2[0] + p3[0]);
double B = (3*p0[0] - 6*p1[0] + 3*p2[0]);
double C = (-3*p0[0] + 3*p1[0]);
double D = p0[0];
double E = (-p0[1] + 3*p1[1] - 3*p2[1] + p3[1]);
double F = (3*p0[1] - 6*p1[1] + 3*p2[1]);
double G = (-3*p0[1] + 3*p1[1]);
double H = p0[1];
//apply the transformation
for(long i = 0; i < OUT.height(); i++){
for(long j = 0; j < OUT.width(); j++){
//t = x / width
double t = trange * j;
//apply the article given formulas
double x_path_d = 3*t*t*A + 2*t*B + C;
double y_path_d = 3*t*t*E + 2*t*F + G;
double angle = 3.14159265/2.0 + std::atan(y_path_d / x_path_d);
mapped_point.Set((t*t*t)*A + (t*t)*B + t*C + D + i*std::cos(angle),
(t*t*t)*E + (t*t)*F + t*G + H + i*std::sin(angle),
1);
//test if the point is inside the image
if(mapped_point[0] < 0 ||
mapped_point[0] >= OUT.width() ||
mapped_point[1] < 0 ||
mapped_point[1] >= IN.height())
continue;
OUT.setPixel(
long(mapped_point[0]),
long(mapped_point[1]),
IN.getPixel(j, i));
}
}
Applying this code in a 300x196 rgb image all i get is a black screen no matter what control points i use, is hard to find information about this kind of transformation, searching for parametric curves all i find is how to draw them, not apply to images. Can someone help me on how to transform an image with a bezier curve?
IMHO applying a curve to an image sound like using a LUT. So you will need to check for the value of the curve for different image values and then switch the image value with the one on the curve, so, create a Look-Up-Table for each possible value in the image (e.g : 0, 1, ..., 255, for a gray value 8 bit image), that is a 2x256 matrix, first column has the values from 0 to 255 and the second one having the value of the curve.

OpenCV - Calculating the correct coordinates using homography matrix

Given a homography matrix H (3x3), how can I find the right coordinate (x,y) in the transformed image? I understand in openCV I can use
perspectiveTransform( obj_corners, scene_corners, H);
where obj_corners are the coordinates in the original image, scene_corners are the coordinates in the result image. And I thought the computation formula should be:
w = H_31 * obj_corners[i].x + H_32 * obj_corners[i].y + H_33 * 1;
scene_corners[i].x = (H_11 * obj_corners[i].x + H_12 * obj_corners[i].y + H_13 * 1) / w;
scene_corners[i].y = (H_21 * obj_corners[i].x + H_22 * obj_corners[i].y + H_23 * 1) / w;
Now I have a transform matrix H as H=[95, 41, 246; 51, 160, 8; 240, 63, 240]. If my obj_corner is (0,0), using the above equation, the scene_corner should be (246/240, 8/240). However, when I use openCV perspectiveTransform() function, the scene_corner is (17.56, -12.98). Why there is a mis-match here?
I got this transforma matrix H from cv::findHomography() function. And I know the answer of (0,0) --> (17.56, -12.98) is correct. I just don't know how to calculate this coordinate. Especially how to get a negative number here.
To be more clearly, here is how I my openCV code look like with a wrong output:
cv::Mat H = cv::findHomography(trackedPoints2, trackedPoints1, CV_RANSAC);
double H11 = H.data[0];
double H12 = H.data[1];
double H13 = H.data[2];
double H21 = H.data[3];
double H22 = H.data[4];
double H23 = H.data[5];
double H31 = H.data[6];
double H32 = H.data[7];
double H33 = H.data[8];
int x = 0, y = 0;
int w = H31 * x + H32 * y + H33;
double x_dst = ((H11 * x + H12 * y + H13) + w / 2) / w;
double y_dst = ((H21 * x + H22 * y + H23) + w / 2) / w;
And here is the openCV code gives correct answer (17.56, -12.98)
cv::Mat H = cv::findHomography(trackedPoints2, trackedPoints1, CV_RANSAC);
std::vector<Point2f> obj_corners(1);
obj_corners[0] = cvPoint(0, 0);
std::vector<Point2f> scene_corners(1);
perspectiveTransform(obj_corners, scene_corners, H);
printf("%f, %f\n", scene_corners[0].x, scene_corners[0].y);
Can someone give a hint on what is wrong here? Thank you.

GDAL setgeotransform()

I have been trying to use GDAL library to add geoinformation to an image. From the documentation from GDAL web page I could find that I could use GDAlSetGeotransform(), for that I need a six parameters GDAL transformation information. Among the six parameters, the x-rotation and y rotation are considered 0 for north up image. But in my case I don't have north up image. So how could I get these rotation values if I have four corner coordinates of the image.
Or there is any other technique to add geoinformation to my image if I have four corner coordinates of the image.
The best way that I found is to estimate a transform matrix using least squares. This takes the following form.
Given N (4+) sets of pixel values and their matching world coordinates, build a pair of transforms. Then solve for the transforms. The following example is shown in octave. If using C++, just use Eigen or OpenCV and build a Moore-Penrose SVD Pseudo-Inverse Solver.
#!/usr/bin/env octave -qf
% Apply Geo Transform
function [Wx, Wy] = Apply_GeoTransform( Px, Py, adfGeoTransform )
% Multiply
Wx = adfGeoTransform(1,2) * Px + adfGeoTransform(1,3) * Py + adfGeoTransform(1,1);
Wy = adfGeoTransform(1,5) * Px + adfGeoTransform(1,6) * Py + adfGeoTransform(1,4);
endfunction
%
% Main Function
%
% Create Sample Inputs
W = [ 0, 50;
50, 0;
0,-50;
-50, 0];
P = [ 0, 0;
10, 0;
10,10;
0,10];
% Build Input Transform (A)
Ai = [P(1,1), P(1,2), 1;
P(2,1), P(2,2), 1;
P(3,1), P(3,2), 1;
P(4,1), P(4,2), 1];
% Build Output Solution Pairs
Bx = [W(1,1);
W(2,1);
W(3,1);
W(4,1)];
By = [W(1,2);
W(2,2);
W(3,2);
W(4,2)];
% Solve For Inverse x and y
Xi = Ai \ Bx;
Yi = Ai \ By;
% Construct Actual Transform
adfGeoTransform = [Xi(3,1), Xi(1,1), Xi(2,1), Yi(3,1), Yi(1,1), Yi(2,1)];
% Test Result
[Wx1, Wy1] = Apply_GeoTransform( 0, 0, adfGeoTransform );
[Wx2, Wy2] = Apply_GeoTransform(10, 0, adfGeoTransform );
[Wx3, Wy3] = Apply_GeoTransform(10,10, adfGeoTransform );
[Wx4, Wy4] = Apply_GeoTransform( 0,10, adfGeoTransform );
[Wx5, Wy5] = Apply_GeoTransform( 5, 5, adfGeoTransform );
printf('W1: %d, %d\n', Wx1, Wy1);
printf('W2: %d, %d\n', Wx2, Wy2);
printf('W3: %d, %d\n', Wx3, Wy3);
printf('W4: %d, %d\n', Wx4, Wy4);
printf('W5: %d, %d\n', Wx5, Wy5);

Extracting subimage with a specified aspect ratio

I need to extract an object from an image. I know the location of the object inside the image, ie the region where the object is located: this region is provided as a pair of coordinates [xmin, ymin] and [xmax, ymax].
I would like to modify the coordinates of this region (thus increasing the height and width in a suitable way) in order to extract a subimage with a specified aspect ratio. So, we have the following constraints:
in order to avoid cutting the object incorrectly, the width and height of the region must not be reduced;
bounds checking: the adaptation of the region size must ensure that the new coordinates are inside the image;
the width/height ratio of the subimage should be approximately equal to the specified aspect ratio.
How to solve this problem?
UPDATE: one possible solution
The solution to my problem is mainly the algorithm proposed by Mark in this answer. The result of this algorithm is a new region wider or higher than the original and it is able to obtain a new aspect ratio very close to that specified, without moving the center of the original region (if this is feasible, depending on the position of the region within the original image). The region obtained from this algorithm could be further processed by the following algorithm in order to make the aspect ratio closer to that specified.
for left=0:(xmin-1), // it tries all possible combinations
for right=0:(imgWidth-xmax), // of increments of the region size
for top=0:(ymin-1), // along the four directions
for bottom=0:(imgHeight-ymax),
x1 = xmin - left;
x2 = xmax + right;
y1 = ymin - top;
y2 = ymax + bottom;
newRatio = (x2 - x1) / (y2 - y1);
if (newRatio == ratio)
rect = [x1 y1 x2 y2];
return;
end
end
end
end
end
Example... An image with 976 rows and 1239 columns; an initial region [xmin ymin xmax ymax] = [570 174 959 957].
First algorithm (main processing).
Input: the initial region and the image size.
Output: it produces new region r1 = [568 174 960 957],
width = 392 and height = 783, so the aspect ratio is equal to 0.5006.
Second algorithm (post-processing).
Input: the region r1.
Output: new region r2 = [568 174 960 958],
width = 392 and height = 784, so the aspect ratio is equal to 0.5.
obj_width = xmax - xmin
obj_height = ymax - ymin
if (obj_width / obj_height > ratio)
{
height_adjustment = ((obj_width / ratio) - (ymax - ymin)) / 2;
ymin -= height_adjustment;
ymax += height_adjustment;
if (ymin < 0)
{
ymax -= ymin;
ymin = 0;
}
if (ymax >= image_height)
ymax = image_height - 1;
}
else if (obj_width / obj_height < ratio)
{
width_adjustment = ((obj_height * ratio) - (xmax - xmin)) / 2;
xmin -= width_adjustment;
xmax += width_adjustment;
if (xmin < 0)
{
xmax -= xmin;
xmin = 0;
}
if (xmax >= image_width)
xmax = image_width - 1;
}
Let's start with your region: a w x h rectangle centered on a point p. You want to extend this region to have the aspect ratio r. The idea is to extend the width or the height:
(trivial case) If w / h == r, then return.
Compute w' = h x r.
If w' > w, then the resulting region is of width w', height h and center p.
Else, the resulting region is of width w, height h' = w / r, and center p.
Move the center p to follow the edges of the image if it has to be clipped, for example if the resulting region upper-left point is outside of the image: let u = upper-left point of the resulting region and d = (min(u.x,0), min(u.y,0)). Then, the final center will be p' = p - d. It is similar for the lower-right part of the region.
Clip the resulting region to the image.