Related
I have a list of 2D coordinates that draw a shape, e.g. [{12, 14}, {22, 44}, {59, 33}, ...]
I'd like to be able to take this shape, and center it in a canvas of arbitrary size (let's say 400x400) and have it take as much space as possible.
I've figured out how to normalize the list so it's in the 0-1 range, but ended up being stuck there when trying to then scale it up to the desired size.
Any help would be appreciated!
Find minimal and maximal values for X and Y coordinates xmin, xmax, ymin, ymax
Calculate point cloud width and height, and middle coordinates
cw = xmax - xmin
ch = ymax - ymin
mx = (xmax + xmin) / 2
my = (ymax + ymin) / 2
Now find coefficient
if cw * canvas.height >= ch * canvas.width
coeff = canvas.width / cw
else
coeff = canvas.height / ch
Now get canvas center
centerx = canvas.width / 2
centery = canvas.height / 2
and apply the next transformation to every point (x,y):
screenx = centerx + coeff * (x - mx)
screeny = centery + coeff * (y - my)
I used YOLOv3 to detect windows on the building. Each window shows with a bounding box around, also I extracted coordinates of each window [top_left, bottom_left, top_right, bottom_right]. now I want to find free space[wall] between all the windows. I put the coordinates of each window in a Dict.
when I set the values manually it works for each window for instance:
p1 = points_list[0][2] # top_left window1
p2 = points_list[1][1] # bottom_left window2
cv2.rectangle(img, p1, p2, (255, 0, 255), -1)
, but how can I make it automatically to find walls.
Here is my first output image, the detected wall shows by pink color.
here also I have added a sample of my code.
def bb_to_rect(x, y, w, h):
top_left = (x, y)
top_right = (x + w, y)
bottom_left = (x, y + h)
bottom_right = (x + w, y + h)
return top_left, bottom_left, top_right, bottom_right
def draw_bounding_box(img, font, boxes, confidences, colors):
indices = cv2.dnn.NMSBoxes(boxes, confidences, CONF_THRESHOLD, NMS_THRESHOLD)
points_list={}
count = 0
for i in range(len(boxes)):
if i in indices:
(x, y, w, h) = boxes[i]
label = "{}:{:.2f}%".format(classes[class_ids[i]], confidences[i] * 100)
color = colors[class_ids[i]]
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
cv2.putText(img, str(i), (x, y), cv2.FONT_HERSHEY_TRIPLEX,
.5, (255, 255, 0), 1, cv2.LINE_AA)
top_left, bottom_left, top_right, bottom_right = bb_to_rect(x, y, w, h)
points_list[count] = [top_left, bottom_left, top_right, bottom_right]
count += 1
cv2.circle(img, (x + w, y), 3, (50, 100, 0), -1) # top Right
cv2.circle(img, (x + w, y + h), 3, (0, 0, 255), -1) # bottom Right
cv2.circle(img, (x, y), 3, (0, 255, 100), -1) # top left
cv2.circle(img, (x, y + h), 3, (255, 0, 255), -1) # bottom left
return img, points_list
if __name__ == "__main__":
net, output_layers, colors, classes = load_yolo_model()
img, height, width = load_img(IMAGES_PATH)
outs = create_blob(img, net, output_layers)
boxes, class_ids, confidences, centroids = detect_obj(outs, height, width, img)
img, points_list = draw_bounding_box(img, FONT, boxes, confidences, colors)
p1 = points_list[0][2] # window0 to window1
p2 = points_list[1][1]
p2_1 = points_list[1][2] # window1 to window2
p2_2 = points_list[2][1]
p3_1 = points_list[3][2] # window3 to window5
p5_2 = points_list[5][1]
cv2.rectangle(img, p1, p2, (255, 0, 255), -1)
cv2.rectangle(img, p2_1, p2_2, (255, 0, 255), -1)
cv2.rectangle(img, p3_1, p5_2, (255, 0, 255), -1)
cv2.imshow('out', img)
cv2.waitKey(0)
Also if you see the photo you will see the windows detected randomly and the Id number for each window does not order. How can I solve this problem? thanks in advance for helping:)
Have you tried Image Segmentation to find the objects on wall? then with a simple trick like finding the countors you can measure the distance between the objects like windows as you said.
2nd trick:
I've got another easy way which may help you:
first of all, find the edges using canny edg detection, the with dilate function fill the middle space of vertical edges between windows then by Hough Line Detection you can find the vertical lines of windows, and get the distance between the y axis of vertical lines of the wall.
I am trying to create the animation in this video using Python. But I stuck on the very first step. Till now I've created a Circle and a point rotating around its circumference. My code is given below. Now I want to plot the y values corresponding to x=np.arange(0, I*np.pi, 0.01) along the x-axis (as shown in update() function in the code). For this I have to define another function to plot these x and y and pass that function inside a new animation.FuncAnimation().
Is there any way to plot everything using only the update() function?
Note I have found a code of this animation in here. But it is written in Java!
My Code
import matplotlib.pyplot as plt
from matplotlib import animation
import numpy as np
W = 6.5
H = 2
radius = 1
I = 2
T = 3
N = 2
plt.style.use(['ggplot', 'dark_background'])
def create_circle(x, y, r):
circle = plt.Circle((x, y), radius=r, fill=False, alpha=0.7, color='w')
return circle
def create_animation():
fig = plt.figure()
ax = plt.axes(xlim=(-2, W + 2), ylim=(-H, H))
circle = create_circle(0, 0, radius)
ax.add_patch(circle)
line1, = ax.plot(0, 1, marker='o', markersize=3, color='pink', alpha=0.7)
def update(theta):
x = radius * np.cos(theta)
y = radius * np.sin(theta)
line1.set_data([0, x], [0, y])
return line1,
anim = []
anim.append(animation.FuncAnimation(fig, update,
frames=np.arange(0, I * np.pi, 0.01),
interval=10, repeat=True))
# anim.append(animation.FuncAnimation(fig, update_line, len(x),
# fargs=[x, y, line, line1], interval=10))
plt.grid(False)
plt.gca().set_aspect('equal')
plt.gca().spines['left'].set_visible(False)
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.gca().spines['bottom'].set_visible(False)
plt.gca().set_xticks([])
plt.gca().set_yticks([])
plt.show()
if __name__ == '__main__':
create_animation()
Edit. I've improved the task by defining a global variable pos and changing the update() function in the following manner ...The animation now looks better but still having bugs!
Improved Portion
plot, = ax.plot([], [], color='w', alpha=0.7)
level = np.arange(0, I * np.pi, 0.01)
num = []
frames = []
for key, v in enumerate(level):
num.append(key)
frames.append(v)
def update(theta):
global pos
x = radius * np.cos(theta)
y = radius * np.sin(theta)
wave.append(y)
plot.set_data(np.flip(level[:pos] + T), wave[:pos])
line1.set_data([0, x], [0, y])
pos += 1
return line1, plot,
Edit Till now I've done the following:
def update(theta):
global pos
x, y = 0, 0
for i in range(N):
prev_x = x
prev_y = y
n = 2 * i + 1
rad = radius * (4 / (n * np.pi))
x += rad * np.cos(n * theta)
y += rad * np.sin(n * theta)
wave.append(y)
circle = create_circle(prev_x, prev_y, rad)
ax.add_patch(circle)
plot.set_data(np.flip(level[:pos] + T), wave[:pos])
line2.set_data([x, T], [y, y])
line1.set_data([prev_x, x], [prev_y, y])
pos += 1
return line1, plot, line2,
Output
Please help to correct this animation. Or, is there any efficient way to do this animation?
Edit Well, now the animation is partially working. But there is a little issue: In my code (inside the definition of update()) I have to add circles centered at (prev_x, prev_y) of radius defined as rad for each frame. For this reason I try to use a for loop in the definition of update() but then all the circles remains in the figure (see the output below). But I want one circle in each frame with the centre and radius as mentioned above. Also the same problem is with the plot. I try to use ax.clear() inside the for loop but it didn't work.
I have a problem where I have to select all squares (think pixels) that are partially within a circle (even if the circle only cuts through a small corner of the square, but not if it goes through one of the corner vertices). The radius is an integer multiple of the pixel size.
The problem is that the center of the circle is between pixels, i.e. on the corner vertices of four pixels.
I want to visit each pixel only once.
For example, I would like to select all white pixels in the following images:
R = 8 px
R = 10 px
For a circle with the center in the center of a pixel, this wouldn't be a problem, and I could use the usual form of the Bresenham algorithm:
public void checkCircle(int x0, int y0, int radius) {
int x = radius;
int y = 0;
int err = -x;
while (x > 0) {
while (err <= 0) {
y++;
err += 2 * y + 1;
}
checkVLine(x0 + x, y0 - y, y0 + y);
checkVLine(x0 - x, y0 - y, y0 + y);
x--;
err -= 2 * x + 1;
}
checkVLine(x0, y0 - radius, y0 + radius);
}
public void checkVLine(int x, int y0, int y1) {
assert(y0 <= y1);
for (int y = y0; y <= y1; y++)
checkPixel(x, y);
}
Sadly, I don't see how to adapt it to support inter-pixel circles.
For the first quadrant - cell should be marked if its left bottom corner lies inside circle, so you can rasterize with simple loops
for dy = 0 to R-1
dx = 0
sq = R * R - dy * dy
while dx * dx < sq
mark (dx, dy)
mark (dx, -dy-1)
mark (-dx-1, dy)
mark (-dx-1, -dy-1)
To fill whole horizontal lines, you can calculate max value for dx
for dy = 0 to R-1
mdx = Floor(Sqrt(R * R - dy * dy))
fill line (-mdx-1,dy)-(mdx,dy)
fill line (-mdx-1,-dy-1)-(mdx,-dy-1)
I am calculating lines (2 sets of coordinates ) ( the purple and green-blue lines ) that are n perpendicular distance from an original line. (original line is pink ) ( distance is the green arrow )
How do I get the coordinates of the four new points?
I have the coordinates of the 2 original points and their angles. ( pink line )
I need it to work if the lines are vertical, or any other orientation.
Right now I am trying to calculate it by:
1. get new point n distance perpendicular to the two old points
2. find where the circle intersects the new line I have defined.
I feel like there is an easier way.
Similarly to #MBo's answer, let's assume that the center is (0,0) and that your initial two points are:
P0 = (x0, y0) and P1 = (x1, y1)
A point on the line P0P1 has the form:
(x, y) = c(x1 - x0, y1 - y0) + (x0, y0)
for some constant c.
Let (u, v) be the normal to the line P0P1:
(u, v) = (y1 - y0, x1 - x0) / sqrt((x1 - x0)^2 + (y1 - y0)^2)
A point on any of the lines parallel to P0P1 has the form:
(x, y) = c(x1 - x0, y1 - y0) + (x0, y0) +/- (u, v)* n {eq 1}
where n is the perpendicular distance between lines and c is a constant.
What remains here is to find the values of c such that (x,y) is on the circle. But these can be calculated by solving the following two quadratic equations:
(c(x1 - x0) + x0 +/- u*n)^2 + (c(y1 - y0) + y0 +/- v*n)^2 = r^2
where r is the radius. Note that these equations can be written as:
c^2(x1 - x0)^2 + 2c(x1 - x0)*(x0 +/- u*n) + (x0 +/- u*n)^2
+ c^2(y1 - y0)^2 + 2c(y1 - y0)*(y0 +/- v*n) + (y0 +/- v*n)^2 = r^2
or
A*c^2 + B*c + D = 0
where
A = (x1 - x0)^2 + (y1 - y0)^2
B = 2(x1 - x0)*(x0 +/- u*n) + 2(y1 - y0)*(y0 +/- v*n)
D = (x0 +/- u*n)^2 + (y0 +/- v*n)^2 - r^2
which are actually two quadratic equations one for each selection of the +/- signs. The 4 solutions of these two equations will give you the four values of c from which you will get the four points using {eq 1}
UPDATE
Here are the two quadratic equations (I've reused the letters A, B and C but they are different in each case):
A*c^2 + B*c + D = 0 {eq 2}
where
A = (x1 - x0)^2 + (y1 - y0)^2
B = 2(x1 - x0)*(x0 + u*n) + 2(y1 - y0)*(y0 + v*n)
D = (x0 + u*n)^2 + (y0 + v*n)^2 - r^2
A*c^2 + B*c + D = 0 {eq 3}
where
A = (x1 - x0)^2 + (y1 - y0)^2
B = 2(x1 - x0)*(x0 - u*n) + 2(y1 - y0)*(y0 - v*n)
D = (x0 - u*n)^2 + (y0 - v*n)^2 - r^2
Let's circle radius is R, circle center is (0,0) (if not, shift all coordinates to simplify math), first chord end is P0=(x0, y0), second chord end is P1=(x1,y1), unknown new chord end is P=(x,y).
Chord length L is
L = Sqrt((x1-x0)^2 + (y1-y0)^2)
Chord ends lie on the circle, so
x^2 + y^2 = R^2 {1}
Doubled area of triangle PP0P1 might be expressed as product of the base and height and through absolute value of cross product of two edge vectors, so
+/- L * n = (x-x0)*(y-y1)-(x-x1)*(y-y0) = {2}
x*y - x*y1 - x0*y + x0*y1 - x*y + x*y0 + x1*y - x1*y0 =
x * (y0-y1) + y * (x1-x0) + (x0*y1-x1*y0)
Solve system of equation {1} and {2}, find coordinates of new chord ends.
(Up to 4 points - two for +L*n case, two for -L*n case)
I cannot claim though that this method is simpler - {2} is essentially an equation of parallel line, and substitution in {1} is intersection with circle.