So I have a pan-tilt system with an airbrush on top, the pressure is quite strong so that I can place the robot at a distance of at least 1.5 meters. I currently have normalized coordinates XY that I can visualize on my camera like this
Now I want to translate those coordinates to a real canvas and allow the pan-tilt to point towards them and eventually spray. The two servos have 0 to 180 degrees but the airbrush is positioned on top of the tilt at 90. So if we consider that the pan and tilt it's at 90 the airbrush points perpendicularly to the real canvas. I am following along with this answer https://stackoverflow.com/a/44269392/13475623
lx = (2 * canvasXpoint / canvasW - 1) * np.tan(fovX / 2)
ly = (-2 * canvasYpoint / canvasH + 1) * np.tan(fovY / 2)
lz = 100
tx = np.cos(pan) * np.cos(tilt) * lx - np.cos(tilt) * np.sin(pan) * ly - np.sin(tilt) * lz
ty = np.sin(pan) * lx + np.cos(pan) * ly
tz = np.cos(pan) * np.sin(tilt) * lx - np.sin(pan) * np.sin(tilt) * ly + np.cos(tilt) * lz
tilt = abs(np.arctan2(tz, tx) )*180 /np.pi
pan = abs(np.arcsin(ty / np.sqrt(tx*tx + ty*ty + tz*tz))) *180 /np.pi
he specifically ask to use fovx and fovy, but i have no idea how to place the, is fovx and fovy the same as the centre of the canvas plus z? which gives the robot position?
this is the entire code:
import numpy as np
import random
import cv2
rect = (0,0,0,0)
startPoint = False
endPoint = False
def on_mouse(event,x,y,flags,params):
global rect,startPoint,endPoint
# get mouse click
if event == cv2.EVENT_LBUTTONDOWN:
if startPoint == True and endPoint == True:
startPoint = False
endPoint = False
rect = (0, 0, 0, 0)
if startPoint == False:
rect = (x, y, 0, 0)
startPoint = True
elif endPoint == False:
rect = (rect[0], rect[1], x, y)
endPoint = True
cap = cv2.VideoCapture(0)
waitTime = 50
#Reading the first frame
(grabbed, frame) = cap.read()
# create a numpy array with coordinates, dtype= np.uint32
points = np.array([
[0.3791454386035252, 0.5089704263689607], [0.4983802415059109, 0.4865878212776629], [0.4191061040406586, 0.4890729258496474], [0.48898375092596835, 0.6904554156787046], [0.41117320428962, 0.6855686449973655], [0.48969027909831686, 0.8806483247709954], [0.4096722346480175, 0.8725103831012889], [0.45146556567120294, 0.216198952126905], [0.6304876750748412, 0.1994776546413951], [0.6406976694235704, 0.1861724655606558], [0.6199918357274865, 0.18561325370105788], [0.6525936779272056, 0.201758477474465], [0.6013198509477334, 0.20041966221830415], [0.6683290543094758, 0.29699362669473495], [0.5645238852104717, 0.3113999818240313], [0.6545654774178274, 0.49620430200480303], [0.5898070573107588, 0.49659117464889346], [0.6592482998457356, 0.6834740545963035], [0.5840631897032319, 0.6828527784533074], [0.6408640096147972, 0.8299668209407426], [0.5829181988101784, 0.8173392725052692], [0.6197806290284397, 0.30050890733295843], [0.8252923243905792, 0.23409826375167195], [0.835683753646597, 0.2185883280832016], [0.8131540844750428, 0.21904862499113367], [0.8506741192799976, 0.2279991219170517], [0.7959142481709739, 0.22725381616179272], [0.8733570624656342, 0.3256920048853457], [0.7652207837892534, 0.3239122878098148], [0.893097550288673, 0.44273291363944955], [0.7346131146711571, 0.4430594635999311], [0.902709244982588, 0.5343829401117663], [0.8520378940615836, 0.543215423861057], [0.7842126810888624, 0.5430821914771806], [0.8496391467917583, 0.7170072127563635], [0.7934480818135997, 0.7157067918591926], [0.8415470663986131, 0.8790693270711738], [0.7969306654944098, 0.8786970205344115], [0.8191112469834433, 0.32444646417244244], [0.4544294400182521, 0.10802826838116084], [0.4652589441860643, 0.09470838455219986], [0.44184697991125976, 0.09401847354478254], [0.4784184639521475, 0.1113126386155105], [0.42270482157448985, 0.10977393520172159], [0.5101597581790689, 0.21719483055184013], [0.39370939342390643, 0.21645334444157344], [0.3703281257159549, 0.34746637604116004]], np.float64)
while(cap.isOpened()):
(grabbed, frame) = cap.read()
cv2.namedWindow('frame')
cv2.setMouseCallback('frame', on_mouse)
panarr=[]
tiltarr=[]
#drawing rectangle
if startPoint == True:
cv2.circle(frame, (rect[0], rect[1]), 2,(255, 0, 255), 2)
if startPoint == True and endPoint == True:
cv2.rectangle(frame, (rect[0], rect[1]), (rect[2], rect[3]), (255, 0, 255), 2)
w = rect[2] - rect[0]
h = rect[3] - rect[1]
canvasW = 120
canvasH = 90
distanceZ = 100
#position machine
screenXCenter = (rect[0] + rect[2]) / 2
screenYCenter = (rect[1] + rect[3]) / 2
pan = tilt = 90
servoXcentrepoint = canvasW / 2
servoYcentrepoint = canvasH / 2
# fov
fovX = np.arctan((canvasW * canvasH )/distanceZ)
for x, y in points:
screenX = (x * w) + rect[0] #- differencesqrx
screenY = (y * h) + rect[1] #- differencesqry
cv2.circle(frame,(int(screenXCenter),int(screenYCenter)),2,(255, 255, 0),2)
cv2.circle(frame,(int(screenX),int(screenY)),2,(255, 45, 250),2)
canvasXpoint = (x * canvasW)
canvasYpoint = (y * canvasH)
# dx = canvasXpoint - servoXcentrepoint
# dy = canvasYpoint - servoYcentrepoint
# pan = abs(np.arctan((distanceZ/dx))) * 180/np.pi
# tilt = abs(np.arctan(distanceZ/dy)) * 180/np.pi
lx = (2 * canvasXpoint / canvasW - 1) * np.tan(servoXcentrepoint / 2)
ly = (-2 * canvasYpoint / canvasH + 1) * np.tan(servoYcentrepoint / 2)
lz = 10
tx = np.cos(pan) * np.cos(tilt) * lx - np.cos(tilt) * np.sin(pan) * ly - np.sin(tilt) * lz
ty = np.sin(pan) * lx + np.cos(pan) * ly
tz = np.cos(pan) * np.sin(tilt) * lx - np.sin(pan) * np.sin(tilt) * ly + np.cos(tilt) * lz
tilt = abs(np.arctan2(tz, tx) )*180 /np.pi
pan = abs(np.arcsin(ty / np.sqrt(tx*tx + ty*ty + tz*tz))) *180 /np.pi
tiltarr.append(int(tilt))
panarr.append(int(pan))
# a = [x,y]
cv2.imshow('frame',frame)
if cv2.waitKey(1)==ord('q'):
break
print(tiltarr)
print(panarr)
cap.release()
cv2.destroyAllWindows()
The ultimate goal is to determine the angle for the pan and tilt based on each point
Related
I have high resolution scans of old navigational maps, that I have turned into map tiles with gdal2tiles. Now I want to write code for a live video stream that records a panning movement between two random points on the maps.
Initially, I had the code working by generating an image for each videoframe, that was assembled from a grid of map tiles. These are small jpg files of 256px x 256px. The next videoframe would then show the same map, but translated a certain amount in the x and y direction.
But opening each jpg with Image.open proved to be a bottleneck. So I tried to make the process faster by reusing the opened tiles until they disappeared off the frame, and only opening fresh tiles as needed. I basically first translate the whole image like so: newframe = oldframe.transform(oldframe.size, Image.AFFINE, data). Then I add a row of fresh tiles on top (or bottom, depending on direction of the panning motion), and on the side.
The problem
I seem to run into some rounding errors (I think), because the opened tiles do not always line up well with the existing, translated content. I have thin lines appearing. But I can't figure out where the rounding errors may come from. Nor how to avoid them.
Here is some code which shows the lines appearing, using colored empty images rather than the map tiles:
import time
import math
from random import randint, uniform
from PIL import Image
import numpy as np
import cv2
FRAMESIZE = (1920, 1080)
TILESIZE = 256
class MyMap:
'''A map class, to hold all its properties'''
def __init__(self) -> None:
self.maxzoom = 8
self.dimensions = (40000, 30000)
self.route, self.zoom = self.trajectory()
pass
def trajectory(self):
'''Calculate random trajectories from map dimensions'''
factor = uniform(1, 8)
extentwidth = FRAMESIZE[0] * factor
extentheight = FRAMESIZE[1] * factor
mapwidth, mapheight = (self.dimensions)
zdiff = math.log2(factor)
newzoom = math.ceil(self.maxzoom - zdiff)
a = (randint(0 + math.ceil(extentwidth / 2), mapwidth - math.floor(extentwidth / 2)),
randint(0 + math.ceil(extentheight / 2), mapheight - math.floor(extentheight / 2))) # Starting point
b = (randint(0 + math.ceil(extentwidth / 2), mapwidth - math.floor(extentwidth / 2)),
randint(0 + math.ceil(extentheight / 2), mapheight - math.floor(extentheight / 2))) # Ending point
x_distance = b[0] - a[0]
y_distance = b[1] - a[1]
distance = math.sqrt((x_distance**2)+(y_distance**2)) # Pythagoras
speed = 3 * factor # pixels per 25th of a second (video framerate)
steps = math.floor(distance / speed)
trajectory = []
for step in range(steps):
x = a[0] + step * x_distance / steps
y = a[1] + step * y_distance / steps
trajectory.append((x, y))
return trajectory, newzoom
def CreateFrame(self, point, oldframe = None):
x = round(self.route[point][0] / (2 ** (self.maxzoom - self.zoom)))
y = round(self.route[point][1] / (2 ** (self.maxzoom - self.zoom)))
if oldframe:
xtrns = x - round(self.route[point - 1][0] / (2 ** (self.maxzoom - self.zoom)))
ytrns = y - round(self.route[point - 1][1] / (2 ** (self.maxzoom - self.zoom)))
# print(x, self.route[point - 1][0] / (2 ** (self.maxzoom - self.zoom)))
west = int(x - FRAMESIZE[0] / 2)
east = int(x + FRAMESIZE[0] / 2)
north = int(y - FRAMESIZE[1] / 2)
south = int(y + FRAMESIZE[1] / 2)
xoffset = west % TILESIZE
yoffset = north % TILESIZE
xrange = range(math.floor(west / TILESIZE), math.ceil(east / TILESIZE))
yrange = range(math.floor(north / TILESIZE), math.ceil(south / TILESIZE))
if oldframe:
data = (
1, #a
0, #b
xtrns, #c +left/-right
0, #d
1, #e
ytrns #f +up/-down
)
newframe = oldframe.transform(oldframe.size, Image.AFFINE, data)
if ytrns < 0:
singlerow_ytile = yrange.start
elif ytrns > 0:
singlerow_ytile = yrange.stop - 1
if ytrns != 0:
for xtile in xrange:
try:
tile = Image.new('RGB', (TILESIZE, TILESIZE), (130,100,10))
newframe.paste(
tile,
(
(xtile - west // TILESIZE) * TILESIZE - xoffset,
(singlerow_ytile - north // TILESIZE) * TILESIZE - yoffset
)
)
except:
tile = None
if xtrns < 0:
singlerow_xtile = xrange.start
elif xtrns > 0:
singlerow_xtile = xrange.stop - 1
if xtrns != 0:
for ytile in yrange:
try:
tile = Image.new('RGB', (TILESIZE, TILESIZE), (200, 200, 200))
newframe.paste(
tile,
(
(singlerow_xtile - west // TILESIZE) * TILESIZE - xoffset,
(ytile - north // TILESIZE) * TILESIZE - yoffset
)
)
except:
tile = None
else:
newframe = Image.new('RGB',FRAMESIZE)
for xtile in xrange:
for ytile in yrange:
try:
tile = Image.new('RGB', (TILESIZE, TILESIZE), (100, 200, 20))
newframe.paste(
tile,
(
(xtile - west // TILESIZE) * TILESIZE - xoffset,
(ytile - north // TILESIZE) * TILESIZE - yoffset
)
)
except:
tile = None
return newframe
def preparedisplay(img, ago):
quit = False
open_cv_image = np.array(img)
# Convert RGB to BGR
open_cv_image = open_cv_image[:, :, ::-1].copy()
# Write some Text
font = cv2.FONT_HERSHEY_TRIPLEX
bottomLeftCornerOfText = (10,1050)
fontScale = 1
fontColor = (2,2,2)
thickness = 2
lineType = 2
cv2.putText(open_cv_image,f"Press q to exit. Frame took {(time.process_time() - ago) * 100}",
bottomLeftCornerOfText,
font,
fontScale,
fontColor,
thickness,
lineType)
return open_cv_image
#===============================================================================
quit = False
while not quit:
currentmap = MyMap()
previousframe = None
step = 0
while step < len(currentmap.route):
start = time.process_time()
currentframe = currentmap.CreateFrame(step, previousframe)
previousframe = currentframe
cv2.imshow('maps', preparedisplay(currentframe, start))
timeleft = (time.process_time() - start)
# print(timeleft)
# if cv2.waitKey(40 - int(timeleft * 100)) == ord('q'):
if cv2.waitKey(1) == ord('q'):
# press q to terminate the loop
cv2.destroyAllWindows()
quit = True
break
step += 1
Im trying to detect the ROI with the most black pixels for the license plate
Below are the code on the number plate. It is based of the question of How to recognize vehicle license / number plate (ANPR) from an image?.
I modified it a bit b
import cv2
import numpy as np
import imutils
import sys
import glob
import math
import time
import os
def validate_contour(contour, img, aspect_ratio_range, area_range):
rect = cv2.minAreaRect(contour)
img_width = img.shape[1]
img_height = img.shape[0]
box = cv2.boxPoints(rect)
box = np.int0(box)
X = rect[0][0]
Y = rect[0][1]
angle = rect[2]
width = rect[1][0]
height = rect[1][1]
angle = (angle + 180) if width < height else (angle + 90)
output = False
if (width > 0 and height > 0) and ((width < img_width / 2.0) and (height < img_width / 2.0)):
aspect_ratio = float(width) / height if width > height else float(height) / width
if (aspect_ratio >= aspect_ratio_range[0] and aspect_ratio <= aspect_ratio_range[1]):
if ((height * width > area_range[0]) and (height * width < area_range[1])):
box_copy = list(box)
point = box_copy[0]
del (box_copy[0])
dists = [((p[0] - point[0]) ** 2 + (p[1] - point[1]) ** 2) for p in box_copy]
sorted_dists = sorted(dists)
opposite_point = box_copy[dists.index(sorted_dists[1])]
tmp_angle = 90
if abs(point[0] - opposite_point[0]) > 0:
tmp_angle = abs(float(point[1] - opposite_point[1])) / abs(point[0] - opposite_point[0])
tmp_angle = rad_to_deg(math.atan(tmp_angle))
if tmp_angle <= 45:
output = True
return output
def deg_to_rad(angle):
return angle * np.pi / 180.0
def rad_to_deg(angle):
return angle * 180 / np.pi
def enhance(img):
kernel = np.array([[-1, 0, 1], [-2, 0, 2], [1, 0, 1]])
return cv2.filter2D(img, -1, kernel)
img = cv2.imread('13.jpg')
input_image = imutils.resize(img, width=500)
raw_image = np.copy(input_image)
img_original = input_image.copy()
img_mask = input_image.copy()
lic_plate = input_image.copy()
contoured = input_image.copy()
gray = cv2.cvtColor(img_original, cv2.COLOR_BGR2GRAY)
gray = enhance(gray)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
gray = cv2.Sobel(gray, -1, 1, 0)
h, sobel = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
se = cv2.getStructuringElement(cv2.MORPH_RECT, (16, 4))
binary = cv2.morphologyEx(sobel, cv2.MORPH_CLOSE, se)
ed_img = np.copy(binary)
contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for contour in contours:
aspect_ratio_range = (2.2, 12) # minimum 2.2 , max 12
area_range = (500, 18000)
rectangles = cv2.minAreaRect(contour) # rect = ((center_x,center_y),(width,height),angle)
boxes = cv2.boxPoints(rectangles) # Find four vertices of rectangle from above rect
boxes = np.int0(boxes) # Round the values and make it integers
# print(box)
all_area = cv2.drawContours(contoured, [boxes], 0, (127, 0, 255), 2)
if validate_contour(contour, binary, aspect_ratio_range, area_range):
rect = cv2.minAreaRect(contour)
box = cv2.boxPoints(rect)
box = np.int0(box)
Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)
angle = rect[2]
if angle < -45:
angle += 90
W = rect[1][0]
H = rect[1][1]
aspect_ratio = float(W) / H if W > H else float(H) / W
center = ((x1 + x2) / 2, (y1 + y2) / 2)
size = (x2 - x1, y2 - y1)
M = cv2.getRotationMatrix2D((size[0] / 2, size[1] / 2), angle, 1.0);
tmp = cv2.getRectSubPix(ed_img, size, center)
tmp = cv2.warpAffine(tmp, M, size)
TmpW = H if H > W else W
TmpH = H if H < W else W
tmp = cv2.getRectSubPix(tmp, (int(TmpW), int(TmpH)), (size[0] / 2, size[1] / 2))
__, tmp = cv2.threshold(tmp, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
sortedplate = cv2.drawContours(img_mask, [box], 0, (127, 0, 255), 2)
white_pixels = 0
for x in range(tmp.shape[0]):
for y in range(tmp.shape[1]):
if tmp[x][y] == 255:
white_pixels += 1
edge_density = float(white_pixels) / (tmp.shape[0] * tmp.shape[1])
tmp = cv2.getRectSubPix(raw_image, size, center)
tmp = cv2.warpAffine(tmp, M, size)
TmpW = H if H > W else W
TmpH = H if H < W else W
tmp = cv2.getRectSubPix(tmp, (int(TmpW), int(TmpH)), (size[0] / 2, size[1] / 2))
# getRectSubPix( = Retrieves a pixel rectangle from an image with sub-pixel accuracy.
if edge_density > 0.5:
cv2.drawContours(input_image, [box], 0, (127, 0, 255), 2)
cv2.imshow('original', img_original)
cv2.imshow('sobel', sobel)
cv2.imshow('binary', binary)
cv2.imshow("all contours", all_area)
cv2.imshow("sorted", sortedplate)
cv2.imshow("detected", lic_plate)
cv2.waitKey(0)
The image The number plate that needed to detect
Example of images lp1 lp2 lp3 lp4
I am currently trying to draw a Mandelbrot set in python with turtle. However, my problem has nothing to do with the Mandelbrot. I can't change the size of my turtle window. How can I do that?
I tried to initialize a screen and set the screen size with the screensize method. Nothing changes if I do this.
This is my code for drawing the set. I pasted the whole code because I don't know what I did wrong that the screen size doesn't change.
from turtle import *
height = 360
width = 360
screen = Screen()
screen.screensize(width, height)
tu = Turtle()
tu.hideturtle()
tu.speed(0)
tu.penup()
def decreasePoint(n, start1, stop1, start2, stop2):
return ((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2
for x in range(width):
for y in range(height):
a = decreasePoint(x, 0, width, -2, 2)
b = decreasePoint(y, 0, height, -2, 2)
ca = a
cb = b
n = 0
z = 0
while n < 100:
aa = a * a - b * b
bb = 2 * a * b
a = aa + ca
b = bb + cb
n += 1
if abs(a + b) > 16:
break
bright = 'pink'
if (n == 100):
bright = 'black'
tu.goto(x , y)
tu.pendown()
tu.dot(4, bright)
tu.penup()
done()
Instead of:
screen.screensize(width, height)
do:
screen.setup(width, height)
The screensize() method sets the amount of area the turtle can roam, but doesn't change the screen size (despite the name), just the scrollable area. Also, to simplify your code, speed it up and produce a more detailed result, I suggest the following rework:
from turtle import Screen, Turtle
WIDTH, HEIGHT = 360, 360
screen = Screen()
screen.setup(WIDTH + 4, HEIGHT + 8) # fudge factors due to window borders & title bar
screen.setworldcoordinates(0, 0, WIDTH, HEIGHT)
turtle = Turtle()
turtle.hideturtle()
turtle.penup()
def scalePoint(n, start1, stop1, start2, stop2):
return (n - start1) * (stop2 - start2) / (stop1 - start1) + start2
screen.tracer(False)
for x in range(WIDTH):
real = scalePoint(x, 0, WIDTH, -2, 2)
for y in range(HEIGHT):
imaginary = scalePoint(y, 0, HEIGHT, -2, 2)
c = complex(real, imaginary)
z = 0j
color = 'pink'
for _ in range(100):
if abs(z) >= 16.0:
break
z = z * z + c
else:
color = 'black'
turtle.goto(x, y)
turtle.dot(1, color)
screen.update()
screen.tracer(True)
screen.exitonclick()
I have a image that has to be cropped around a bounding box and resized to 256x256. In my original image I have an number of Points (x,y) that are in the bounding box.
This is my original image with my original coordinates marked:
Heres the cropped result, where the red points are the right x,y and the blue ones are my current result:
Heres how I'm doing it:
import numpy as np
import cv2
def scaleBB(bb, scale):
centerX = (bb[0][0] + bb[1][0]) / 2
centerY = (bb[0][1] + bb[2][1]) / 2
center = (centerX, centerY)
scl_center = (centerX * scale[0], centerY * scale[1])
p1 = scale * (bb[0] - center) + scl_center
p2 = scale * (bb[1] - center) + scl_center
p3 = scale * (bb[2] - center) + scl_center
p4 = scale * (bb[3] - center) + scl_center
return np.array([p1, p2, p3, p4])
def expandBB(scaledBB, size):
bbw = np.abs(scaledBB[0][0] - scaledBB[1][0])
bbh = np.abs(scaledBB[0][1] - scaledBB[2][1])
expandX = (size[0] - bbw) / 2
expandY = (size[1] - bbh) / 2
p1 = scaledBB[0] + (-expandX, -expandY)
p2 = scaledBB[1] + (+expandX, -expandY)
p3 = scaledBB[2] + (+expandX, +expandY)
p4 = scaledBB[3] + (+expandX, +expandY)
return np.array([p1, p2, p3, p4])
def recalculate_joints_points(oldX, oldY, newX, newY, joints):
R_x = newX / oldX
R_y = newY / oldY
new_joints = []
for index, joint in enumerate(joints):
x = joint[0]
y = joint[1]
n_x = round(R_x * x)
n_y = round(R_y * y)
print(R_x, R_y, x, y, n_x, n_y)
new_joints.append([n_x, n_y])
return np.array(new_joints)
def cropAndResizeImage(label, bb):
img_path = "original.jpg"
# downscale
image = cv2.imread(img_path)
# orgSize = image.shape[:2]
label = label
bb = bb
print(bb)
dim = int(256 / 2)
# define the target height of the bounding box
targetHeight = 200.0
w = np.abs(bb[0][0] - bb[1][0])
h = np.abs(bb[0][1] - bb[2][1])
targetScale = targetHeight / h
print(targetScale)
scaledImage = cv2.resize(image, (0, 0), fx=targetScale, fy=targetScale)
scaledBB = scaleBB(bb, (targetScale, targetScale))
cropRegion = expandBB(scaledBB, (256, 256))
print(scaledBB)
print(cropRegion)
startX = int(cropRegion[0][0] + dim)
startY = int(cropRegion[0][1] + dim)
endX = startX + 256 # cropRegion[2][0] + dim
endY = startY + 256 #cropRegion[2][1] + dim
print(startX, startY, endX, endY)
padded_image = np.pad(scaledImage, ((dim, dim), (dim, dim), (0, 0)), mode='constant')
croppedImage = padded_image[startY:endY, startX:endX]
# new label
print(image.shape, croppedImage.shape)
oldWidth = image.shape[1]
oldHeight = image.shape[0]
newWidth = 256 + dim
newHeight = 256 + dim
out_label = recalculate_joints_points(oldWidth, oldHeight, newWidth, newHeight, label)
return [croppedImage, out_label]
def main():
labels = np.array([[1214, 598],
[1169, 424],
[1238, 273],
[1267, 285],
[1212, 453],
[1229, 622],
[1253, 279],
[1173, 114],
[1171, 113],
[1050, 60],
[1106, 143],
[1140, 100],
[1169, 80],
[1176, 148],
[1152, 280],
[1087, 391]])
bb = np.array([[1050, 60],
[1267, 60],
[1267, 622],
[1050, 622]])
img, label = cropAndResizeImage(labels, bb)
for point in label:
print(point)
x,y = point
cv2.circle(img,(int(x),int(y)),5,(255,0,0),-11)
cv2.imshow("cropped", img)
cv2.waitKey()
if __name__ == '__main__':
main()
As far as I understood is to get the new (x,y) you have to calculate the ratio (difference of size in a scale factor) but it still seems off. Any help is appreciated.
EDIT 1:
Using as newHeight/Width just 256 produces this image:
*EDIT 2:
Using solution of #ChrisH its quite perfect but still a little bit off:
Here is a function that will translate directly from the original coordinates into the cropped and scaled coordinates. You can skip all the other functions and transform points directly with this
def getNewCoords(x,y):
bbUpperLeftX = bb[0][0]
bbUpperLeftY = bb[0][1]
bbLowerRightX = bb[2][0]
bbLowerRightY = bb[2][1]
sizeX = bbLowerRightX - bbUpperLeftX
sizeY = bbLowerRightY - bbUpperLeftY
sizeMax = max(sizeX, sizeY)
centerX = (bbLowerRightX + bbUpperLeftX)/2
centerY = (bbLowerRightY + bbUpperLeftY)/2
offsetX = (centerX-sizeMax/2)*256/sizeMax
offsetY = (centerY-sizeMax/2)*256/sizeMax
x = x * 256/sizeMax - offsetX
y = y * 256/sizeMax - offsetY
return (x,y)
Since you define
endX = startX + 256
endY = startY + 256
And make the output image as
croppedImage = padded_image[startY:endY, startX:endX]
Shouldn’t the new width and height be 256? instead you define them as
newWidth = 256 + dim
newHeight = 256 + dim
I think dim is unnecessary here
You can use augmentit to do the task.
pip install augmentit
Documentation
link : https://github.com/sandesha-hegde/augmentit
I'm trying to implement SVG path calculations in Python, but I'm running into problems with Arc curves.
I think the problem is in the conversion from end point to center parameterization, but I can't find the problem. You can find notes on how to implement it in section F6.5 of the SVG specifications. I've also looked at implementations in other languages and I can't see what they do different either.
My Arc object implementation is here:
class Arc(object):
def __init__(self, start, radius, rotation, arc, sweep, end):
"""radius is complex, rotation is in degrees,
large and sweep are 1 or 0 (True/False also work)"""
self.start = start
self.radius = radius
self.rotation = rotation
self.arc = bool(arc)
self.sweep = bool(sweep)
self.end = end
self._parameterize()
def _parameterize(self):
# Conversion from endpoint to center parameterization
# http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
cosr = cos(radians(self.rotation))
sinr = sin(radians(self.rotation))
dx = (self.start.real - self.end.real) / 2
dy = (self.start.imag - self.end.imag) / 2
x1prim = cosr * dx + sinr * dy
x1prim_sq = x1prim * x1prim
y1prim = -sinr * dx + cosr * dy
y1prim_sq = y1prim * y1prim
rx = self.radius.real
rx_sq = rx * rx
ry = self.radius.imag
ry_sq = ry * ry
# Correct out of range radii
radius_check = (x1prim_sq / rx_sq) + (y1prim_sq / ry_sq)
if radius_check > 1:
rx *= sqrt(radius_check)
ry *= sqrt(radius_check)
rx_sq = rx * rx
ry_sq = ry * ry
t1 = rx_sq * y1prim_sq
t2 = ry_sq * x1prim_sq
c = sqrt((rx_sq * ry_sq - t1 - t2) / (t1 + t2))
if self.arc == self.sweep:
c = -c
cxprim = c * rx * y1prim / ry
cyprim = -c * ry * x1prim / rx
self.center = complex((cosr * cxprim - sinr * cyprim) +
((self.start.real + self.end.real) / 2),
(sinr * cxprim + cosr * cyprim) +
((self.start.imag + self.end.imag) / 2))
ux = (x1prim - cxprim) / rx
uy = (y1prim - cyprim) / ry
vx = (-x1prim - cxprim) / rx
vy = (-y1prim - cyprim) / ry
n = sqrt(ux * ux + uy * uy)
p = ux
theta = degrees(acos(p / n))
if uy > 0:
theta = -theta
self.theta = theta % 360
n = sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy))
p = ux * vx + uy * vy
if p == 0:
delta = degrees(acos(0))
else:
delta = degrees(acos(p / n))
if (ux * vy - uy * vx) < 0:
delta = -delta
self.delta = delta % 360
if not self.sweep:
self.delta -= 360
def point(self, pos):
if self.arc == self.sweep:
angle = radians(self.theta - (self.delta * pos))
else:
angle = radians(self.delta + (self.delta * pos))
x = sin(angle) * self.radius.real + self.center.real
y = cos(angle) * self.radius.imag + self.center.imag
return complex(x, y)
You can test this with the following code that will draw the curves with the Turtle module. (The raw_input() at the end is just to that the screen doesn't disappear when the program exits).
arc1 = Arc(0j, 100+50j, 0, 0, 0, 100+50j)
arc2 = Arc(0j, 100+50j, 0, 1, 0, 100+50j)
arc3 = Arc(0j, 100+50j, 0, 0, 1, 100+50j)
arc4 = Arc(0j, 100+50j, 0, 1, 1, 100+50j)
import turtle
t = turtle.Turtle()
t.penup()
t.goto(0, 0)
t.dot(5, 'red')
t.write('Start')
t.goto(100, 50)
t.dot(5, 'red')
t.write('End')
t.pencolor = t.color('blue')
for arc in (arc1, arc2, arc3, arc4):
t.penup()
p = arc.point(0)
t.goto(p.real, p.imag)
t.pendown()
for x in range(1,101):
p = arc.point(x*0.01)
t.goto(p.real, p.imag)
raw_input()
The issue:
Each of these four arcs drawn should draw from the Start point to the End point. However, they are drawn from the wrong points. Two curves go from end to start, and two goes from 100,-50 to 0,0 instead of from 0,0 to 100, 50.
Part of the problem is that the implementation notes give you the formula from how to do the conversion form endpoints to center, but doesn't explain what it does geometrically, so I'm not all clear on what each step does. An explanation of that would also be helpful.
I think I have found some errors in your code:
theta = degrees(acos(p / n))
if uy > 0:
theta = -theta
self.theta = theta % 360
The condition uy > 0 is wrong, correct is uy < 0 (the directed angle from (1, 0) to (ux, uy) is negative if uy < 0):
theta = degrees(acos(p / n))
if uy < 0:
theta = -theta
self.theta = theta % 360
Then
if self.arc == self.sweep:
angle = radians(self.theta - (self.delta * pos))
else:
angle = radians(self.delta + (self.delta * pos))
The distinction is not necessary here, the sweep and arc parameters are already accounted for in theta and delta. This can be simplified to:
angle = radians(self.theta + (self.delta * pos))
And finally
x = sin(angle) * self.radius.real + self.center.real
y = cos(angle) * self.radius.imag + self.center.imag
Here sin and cos are mixed up, correct is
x = cos(angle) * self.radius.real + self.center.real
y = sin(angle) * self.radius.imag + self.center.imag
After these modifications, the program runs as expected.
EDIT: There is one more problem. The point method does not account for a possible rotation parameter. The following version should be correct:
def point(self, pos):
angle = radians(self.theta + (self.delta * pos))
cosr = cos(radians(self.rotation))
sinr = sin(radians(self.rotation))
x = cosr * cos(angle) * self.radius.real - sinr * sin(angle) * self.radius.imag + self.center.real
y = sinr * cos(angle) * self.radius.real + cosr * sin(angle) * self.radius.imag + self.center.imag
return complex(x, y)
(See formula F.6.3.1 in the SVG specification.)
Maybe you could have a look at links below, there seems to be a step-by-step guide on how to compute the arcs (see computeArc()):
http://svn.apache.org/repos/asf/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/ext/awt/geom/ExtendedGeneralPath.java
or
http://java.net/projects/svgsalamander/sources/svn/content/trunk/svg-core/src/main/java/com/kitfox/svg/pathcmd/Arc.java