In Android, I used the following code to generate a gradient background that I need:
<gradient
android:angle="90"
android:startColor="#40000000"
android:endColor="#00000000"
android:type="linear" />
The background goes from light to relatively dark from top to bottom. I wonder how to do the same in Python with PIL, since I need the same effect on another program written in Python.
Here's something that shows ways to draw multicolor rectangular horizontal and vertical gradients.
rom PIL import Image, ImageDraw
BLACK, DARKGRAY, GRAY = ((0,0,0), (63,63,63), (127,127,127))
LIGHTGRAY, WHITE = ((191,191,191), (255,255,255))
BLUE, GREEN, RED = ((0, 0, 255), (0, 255, 0), (255, 0, 0))
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
class Rect(object):
def __init__(self, x1, y1, x2, y2):
minx, maxx = (x1,x2) if x1 < x2 else (x2,x1)
miny, maxy = (y1,y2) if y1 < y2 else (y2,y1)
self.min = Point(minx, miny)
self.max = Point(maxx, maxy)
width = property(lambda self: self.max.x - self.min.x)
height = property(lambda self: self.max.y - self.min.y)
def gradient_color(minval, maxval, val, color_palette):
""" Computes intermediate RGB color of a value in the range of minval
to maxval (inclusive) based on a color_palette representing the range.
"""
max_index = len(color_palette)-1
delta = maxval - minval
if delta == 0:
delta = 1
v = float(val-minval) / delta * max_index
i1, i2 = int(v), min(int(v)+1, max_index)
(r1, g1, b1), (r2, g2, b2) = color_palette[i1], color_palette[i2]
f = v - i1
return int(r1 + f*(r2-r1)), int(g1 + f*(g2-g1)), int(b1 + f*(b2-b1))
def horz_gradient(draw, rect, color_func, color_palette):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
width = float(rect.width) # Cache.
for x in range(rect.min.x, rect.max.x+1):
f = (x - rect.min.x) / width
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
draw.line([(x, rect.min.y), (x, rect.max.y)], fill=color)
def vert_gradient(draw, rect, color_func, color_palette):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
height = float(rect.height) # Cache.
for y in range(rect.min.y, rect.max.y+1):
f = (y - rect.min.y) / height
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
draw.line([(rect.min.x, y), (rect.max.x, y)], fill=color)
if __name__ == '__main__':
# Draw a three color vertical gradient.
color_palette = [BLUE, GREEN, RED]
region = Rect(0, 0, 730, 350)
width, height = region.max.x+1, region.max.y+1
image = Image.new("RGB", (width, height), WHITE)
draw = ImageDraw.Draw(image)
vert_gradient(draw, region, gradient_color, color_palette)
image.show()
#image.save("vert_gradient.png", "PNG")
#print('image saved')
And here's the image it generates and displays:
This calculates the intermediate colors in the RGB color space, but other colorspaces could be used — for examples compare results of my answers to the question Range values to pseudocolor.
This could easily be extended to generate RGBA (RGB+Alpha) mode images.
If you only need two colours, this can be done very simply:
def generate_gradient(
colour1: str, colour2: str, width: int, height: int) -> Image:
"""Generate a vertical gradient."""
base = Image.new('RGB', (width, height), colour1)
top = Image.new('RGB', (width, height), colour2)
mask = Image.new('L', (width, height))
mask_data = []
for y in range(height):
mask_data.extend([int(255 * (y / height))] * width)
mask.putdata(mask_data)
base.paste(top, (0, 0), mask)
return base
This creates a layer in each colour, then creates a mask with transparency varying according to the y position. You can replace y / height in line 10 with x / width for a horizontal gradient, or any function of x and y for another gradient.
Here is the technique spelled out. You need 2 layers on top of each other, one for each color. Then you make the transparency for each increasing for the top layer and decreasing for the bottom layer. For extra homework you can change the rate of transparency to an ascending logarithmic scale rather than linear. Have fun with it.
Making some modifications to #martineau's code, this function handles gradient orientation in degrees (not only vertical or horizontal):
from PIL import Image
import math
BLACK, DARKGRAY, GRAY = ((0,0,0), (63,63,63), (127,127,127))
LIGHTGRAY, WHITE = ((191,191,191), (255,255,255))
BLUE, GREEN, RED = ((0, 0, 255), (0, 255, 0), (255, 0, 0))
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
def rot_x(self, degrees):
radians = math.radians(degrees)
return self.x * math.cos(radians) + self.y * math.sin(radians)
class Rect(object):
def __init__(self, x1, y1, x2, y2):
minx, maxx = (x1,x2) if x1 < x2 else (x2,x1)
miny, maxy = (y1,y2) if y1 < y2 else (y2,y1)
self.min = Point(minx, miny)
self.max = Point(maxx, maxy)
def min_max_rot_x(self, degrees):
first = True
for x in [self.min.x, self.max.x]:
for y in [self.min.y, self.max.y]:
p = Point(x, y)
rot_d = p.rot_x(degrees)
if first:
min_d = rot_d
max_d = rot_d
else:
min_d = min(min_d, rot_d)
max_d = max(max_d, rot_d)
first = False
return min_d, max_d
width = property(lambda self: self.max.x - self.min.x)
height = property(lambda self: self.max.y - self.min.y)
def gradient_color(minval, maxval, val, color_palette):
""" Computes intermediate RGB color of a value in the range of minval
to maxval (inclusive) based on a color_palette representing the range.
"""
max_index = len(color_palette)-1
delta = maxval - minval
if delta == 0:
delta = 1
v = float(val-minval) / delta * max_index
i1, i2 = int(v), min(int(v)+1, max_index)
(r1, g1, b1), (r2, g2, b2) = color_palette[i1], color_palette[i2]
f = v - i1
return int(r1 + f*(r2-r1)), int(g1 + f*(g2-g1)), int(b1 + f*(b2-b1))
def degrees_gradient(im, rect, color_func, color_palette, degrees):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
min_d, max_d = rect.min_max_rot_x(degrees)
range_d = max_d - min_d
for x in range(rect.min.x, rect.max.x + 1):
for y in range(rect.min.y, rect.max.y+1):
p = Point(x, y)
f = (p.rot_x(degrees) - min_d) / range_d
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
im.putpixel((x, y), color)
def gradient_image(color_palette, degrees):
region = Rect(0, 0, 600, 400)
width, height = region.max.x+1, region.max.y+1
image = Image.new("RGB", (width, height), WHITE)
degrees_gradient(image, region, gradient_color, color_palette, -degrees)
return image
This flexibility comes at the cost of having to set colors pixel by pixel instead of using lines.
Related
I want to make a traffic simulator of a city and for that I need to get the exact location of the streets and then plot them.
To draw the map I'm taking the streets of omsnx, but these have some flaws.Here I show how it looks like using the omsnx.plot_graph() function.
But using the values of G.edges() and building the graph myself there are inconsistencies like extra streets. This is an example of what the graph looks like using exactly the values provided by the edges.
Note that both images show the same traffic circle.
So the question is how to obtain the real street values without noise, i.e. without several streets playing the role of one.
This is the code I use to plot with pygame.
import osmnx as ox
address_name='Ciudad Deportiva, Havana, Cuba'
#Import graph
point = (23.1021, -82.3936)
G = ox.graph_from_point(point, dist=1000, retain_all=True, simplify=True, network_type='drive')
G = ox.project_graph(G)
import pygame
from map import Map
RED = (255, 0, 0)
BLUE = (0, 0, 20)
GRAY = (215,215,215)
WHITE = (255, 255, 255)
inc = 0.8
m = Map(1400, 800, lng=inc*2555299.469922482, lat=inc*356731.10053785384, i_zoom=0.1)
last_x = -1
last_y = -1
i=0
while True:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 4:
m.inc_zoom()
elif event.button == 5:
m.dec_zoom()
if event.type == pygame.MOUSEMOTION:
if event.buttons[0] == 1:
if last_x == last_y == -1:
last_x, last_y = event.pos
else:
m.x += (event.pos[0] - last_x)/2
m.y += (event.pos[1] - last_y)/2
last_x, last_y = event.pos
if event.type == pygame.MOUSEBUTTONUP:
if event.button == 1:
last_x = last_y = -1
m.fill(GRAY)
# Here I take the coordinates of the streets and paint them
for _, _, data in G.edges(data=True):
try:
m.draw_road(data['geometry'], inc, BLUE)
except:
pass
m.update()
Other functions and objects I use for plotting. This is the code of map.py
from typing import List, Tuple
from window import Window
from pygame import gfxdraw
from shapely.geometry import LineString
def build_rect(
start: Tuple[float,float],
end: Tuple[float,float],
width: float = 3
) -> List[Tuple[float,float]]:
x0, y0 = start
x1, y1 = end
if x0**2 + y0**2 > x1**2 + y1**2:
x0, y0 = end
x1, y1 = start
# vector from start to end
vX, vY = x1 - x0, y1 - y0
# normal vector
nX, nY = -vY, vX
# normalize
n = (nX**2 + nY**2)**0.5
nX, nY = width/n * nX, width/n * nY
# third vector
x2, y2 = x1 + nX, y1 + nY
# fourth vector
x3, y3 = x0 + nX, y0 + nY
return [(x0, y0), (x1, y1), (x2, y2), (x3, y3)]
class Map(Window):
def __init__(self, width, height, **kwargs):
super().__init__(width, height, **kwargs)
def draw_road(self, st: LineString, inc: float, color: Tuple[float,float,float]):
last = None
for c in st.__geo_interface__['coordinates']:
c = (inc*c[0], inc*c[1])
if last == None:
last = (c[0] - self.lat,c[1] - self.lng)
continue
lat = c[0] - self.lat
lng = c[1] - self.lng
pts = build_rect(last, (lat, lng))
gfxdraw.filled_polygon(self.screen, [
(self.x + lat * self.zoom, self.y + lng * self.zoom)
for lat, lng in pts
], color)
And this is the code of window.py.
import pygame
from pygame import gfxdraw
from pygame.locals import *
class Window:
def __init__(self, width, height, **kwargs):
self.width = width
self.height = height
self.zoom = 1
self.x = self.y = 0
self.i_zoom = 0.001
self.__dict__.update(kwargs)
pygame.init()
self.screen = pygame.display.set_mode((width, height))
def inc_zoom(self):
self.zoom += self.i_zoom
def dec_zoom(self):
self.zoom -= self.i_zoom
self.zoom = max(0, self.zoom)
def draw_polygon(self, points, color):
gfxdraw.filled_polygon(self.screen, [
(self.x + pt[0] * self.zoom, self.y + pt[1] * self.zoom)
for pt in points], color)
def fill(self, color):
self.screen.fill(color)
def update(self):
pygame.display.update()
I have a problem.
I have an object detection model that detects two classes, what I want to do is:
Detect class 1 (say c1) on source image (640x640) Draw bounding box and crop bounding box -> (c1 image) and then resize it to (640x640) (DONE)
Detect class 2 (say c2) on c1 image (640x640) (DONE)
Now I want to draw bounding box of c2 on source image
I have tried to explain it here by visualizing it
how can I do it? please help.
Code:
frame = self.REC.ImgResize(frame)
frame, score1, self.FLAG1, x, y, w, h = self.Detect(frame, "c1")
if self.FLAG1 and x > 0 and y > 0:
x1, y1 = w,h
cv2.rectangle(frame, (x, y), (w, h), self.COLOR1, 1)
c1Img = frame[y:h, x:w]
c1Img = self.REC.ImgResize(c1Img)
ratio = c2Img.shape[1] / float(frame.shape[1])
if ratio > 0.35:
c2Img, score2, self.FLAG2, xN, yN, wN, hN = self.Detect(c1Img, "c2")
if self.FLAG2 and xN > 0 and yN > 0:
# What should be the values for these => (__, __),(__,__)
cv2.rectangle(frame, (__, __), (__, __), self.COLOR2, 1)
I had tried a way which could only solve (x,y) coordinates but width and height was a mess
what I tried was
first found the rate of width and height at which the cropped c1 image increased after resize.
for example
x1 = 329
y1 = 102
h1 = 637
w1 = 630
r_w = 630 / 640 # 0.9843
r_h = 637 / 640 # 0.9953
x2 = 158
y2 = 393
h2 = 499
w2 = 588
new_x2 = 158 * 0.9843 # 156
new_y2 = 389 * 0.9953 # 389
new_x2 = x1 + new_x2
new_y2 = y1 + new_y2
this work to find (x,y)
but I am still trying to find a way to get (w,h) of the bounding box.
EDIT
The complete code is:
import cv2
import random
import numpy as np
import onnxruntime as ort
cuda = False
w = "models/model.onnx"
providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] if cuda else ['CPUExecutionProvider']
session = ort.InferenceSession(w, providers=providers)
names = ['face', 'glasses']
colors = {name:[random.randint(0, 255) for _ in range(3)] for name in names}
img = cv2.imread("test.jpg")
def ImgResize(image, width = 640, height = 640, inter = cv2.INTER_CUBIC):
if image is not None:
resized = cv2.resize(image, (width,height), interpolation = inter)
return resized
def Detect(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):
flag = False
w, h = 0, 0
x, y = 0, 0
score = 0
try:
if im is None:
raise Exception(IOError())
shape = im.shape[:2]
if isinstance(new_shape, int):
new_shape = (new_shape, new_shape)
ratio = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
if not scaleup:
ratio = min(ratio, 1.0)
new_unpad = int(round(shape[1] * ratio)), int(round(shape[0] * ratio))
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]
if auto:
dw, dh = np.mod(dw, stride), np.mod(dh, stride)
dw /= 2
dh /= 2
if shape[::-1] != new_unpad:
im = cv2.resize(im, new_unpad, interpolation=cv2.INTER_LINEAR)
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)
image_ = im.transpose((2, 0, 1))
image_ = np.expand_dims(image_, 0)
image_ = np.ascontiguousarray(image_)
im = image_.astype(np.float32)
im /= 255
outname = [i.name for i in session.get_outputs()]
inname = [i.name for i in session.get_inputs()]
inp = {inname[0]:im}
outputs = session.run(outname, inp)[0]
return im, outputs, ratio, (dw, dh)
except IOError:
print("Invalid Image File")
def Detection(img, c_name):
score = 0
name = ""
a, b, c, d = 0, 0, 0, 0
image_, outputs, ratio, dwdh = Detect(img)
ori_images = [img.copy()]
for batch_id, x0, y0, x1, y1, cls_id, score in outputs:
img = ori_images[int(batch_id)]
box = np.array([x0, y0, x1, y1])
box -= np.array(dwdh * 2)
box /= ratio
box = box.round().astype(np.int32).tolist()
cls_id = int(cls_id)
score = round(float(score), 3)
if score > 0.55:
name = names[cls_id]
if name != c_name:
return img, 0, False, 0, 0, 0, 0, "Could Not Detect"
flag = True
a, b, c, d = tuple(box)
score = round(score * 100, 0)
return img, score, flag, a, b, c, d, name
COLORF = (212, 15, 24)
COLORG = (25, 240, 255)
nameW = "Det"
flagF, flagN = False, False
img = ImgResize(img)
c1_img, score, flagF, x1,y1,w1,h1,name = Detection(img,"face")
print(score, flagF, x1,y1,w1,h1,name)
if flagF:
cv2.rectangle(img, (x1,y1), (w1,h1), COLORF, 1)
cv2.putText(img, name, (x1,y1),cv2.FONT_HERSHEY_PLAIN, 2,COLORF,2)
cv2.imshow("face", img)
c1_img = c1_img[y1:h1,x1:w1]
c1_img_orig = c1_img.copy()
c1_img = ImgResize(c1_img)
c2_img, score, flagG, x2,y2,w2,h2,name = Detection(c1_img,"glasses")
if flagG:
c2_img = c2_img[y2:h2,x2:w2]
cv2.rectangle(c1_img_orig, (x2,y2), (w2,h2), COLORG, 1)
cv2.putText(c1_img_orig, name, (x1,y1),cv2.FONT_HERSHEY_PLAIN, 2,COLORG,2)
cv2.imshow("glasses", c2_img)
x3 = x1 + int(x2 * w1 / 640)
y3 = y1 + int(y2 * h1 / 640)
w3 = int(w2 * w1 / 640)
h3 = int(h2 * h1 / 640)
cv2.rectangle(img, (x3,y3), (w3,h3), COLORG, 1)
cv2.imshow(nameW, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
what this code does is for some images it draws the bounding box as required:
but for other images and in video stream this is what happens:
Here is a complete programming example. Please keep in mind that for cv2.rectangle you need to pass top-left corner and bottom-right corner of the rectangle. As you didn't share ImgResize and Detect I made some assumptions:
import cv2
import numpy as np
COLOR1 = (0, 255, 0)
COLOR2 = (0, 0, 255)
DETECT_c1 = (40, 20, 120, 160)
DETECT_c2 = (20, 120, 160, 40)
RESIZE_x, RESIZE_y = 200, 200
frame = np.zeros((RESIZE_y, RESIZE_x, 3), np.uint8)
x1, y1, w1, h1 = DETECT_c1
c1Img = frame[y1:h1, x1:w1]
cv2.rectangle(frame, (x1, y1), (x1 + w1, y1 + h1), COLOR1, 1)
c1Img = cv2.resize(c1Img, (RESIZE_x, RESIZE_y))
x2, y2, w2, h2 = DETECT_c2
x3 = x1 + int(x2 * w1 / RESIZE_x)
y3 = y1 + int(y2 * h1 / RESIZE_y)
w3 = int(w2 * w1 / RESIZE_x)
h3 = int(h2 * h1 / RESIZE_y)
cv2.rectangle(frame, (x3, y3), (x3 + w3, y3 + h3), COLOR2, 1)
cv2.imwrite('out.png', frame)
Output:
I suggest that you treat your bounding box coordinates relatively.
If I understand correctly, your problem is that you have different referential. One way to bypass that is to normalize at each step your bbox coordinates.
c1_box is relative to your image, so :
c1_x = x/640
c1_y = y/640
When you crop, you can record the ratio values between main image and your cropped object.
image_vs_c1_x = c1_x / img_x
image_vs_c1_y = c1_y / img_y
Then you need to multiply your c2 bounding box coordinates by those ratios.
this is how I was able to solve it.
rwf = round((w1-x1)/640, 2)
rhf = round((h1-y1)/640, 2)
x3 = int(x2*rwf )
y3 = int(y2*rhf)
w3 = int(w2*rwf)
h3 = int(h2*rhf)
# these are the top right and bottom left cooridinates
x4 = x1 + x3
y4 = y1 + y3
w4 = x1 + w3
h4 = y1 + h3
So I created this parabola class which can be instantiated with 3 parameters (a, b and c) or with 3 points belonging to the parabola. The punti() function returns all the points belonging to the parabola in a range defined by n and m. Here's the code (Most of this is in Italian, sorry):
class Parabola:
def __init__(self, tipo=0, *params):
'''
Il tipo è 0 per costruire la parabola con a, b, c; 1 per costruire la parabola con
tre punti per la quale passa
'''
if tipo == 0:
self.__a = params[0]
self.__b = params[1]
self.__c = params[2]
self.__delta = self.__b ** 2 - (4 * self.__a * self.__c)
elif tipo == 1:
matrix_a = np.array([
[params[0][0]**2, params[0][0], 1],
[params[1][0]**2, params[1][0], 1],
[params[2][0]**2, params[2][0], 1]
])
matrix_b = np.array([params[0][1], params[1][1], params[2][1]])
matrix_c = np.linalg.solve(matrix_a, matrix_b)
self.__a = round(matrix_c[0], 2)
self.__b = round(matrix_c[1], 2)
self.__c = round(matrix_c[2], 2)
self.__delta = self.__b ** 2 - (4 * self.__a * self.__c)
def trovaY(self, x):
y = self.__a * x ** 2 + self.__b * x + self.__c
return y
def punti(self, n, m, step=1):
output = []
for x in range(int(min(n, m)), int(max(n, m)) + 1, step):
output.append((x, self.trovaY(x)))
return output
Now my little game is about shooting targets with a bow and i have to use the parabola for the trajectory and it passes by 3 points:
The player center
A point with the cursor's x and player's y
A point in the middle with the cursors's y
The trajectory is represented by a black line but it clearly doesn't work and I can't understand why. Here's the code of the game (Don't mind about the bow's rotation, I still have to make it function properly):
import os
import sys
import pygame
from random import randint
sys.path.insert(
1, __file__.replace("pygame-prototype\\" + os.path.basename(__file__), "coniche\\")
)
import parabola
# Initialization
pygame.init()
WIDTH, HEIGHT = 1024, 576
screen = pygame.display.set_mode((WIDTH, HEIGHT))
# Function to rotate without losing quality
def rot_from_zero(surface, angle):
rotated_surface = pygame.transform.rotozoom(surface, angle, 1)
rotated_rect = rotated_surface.get_rect()
return rotated_surface, rotated_rect
# Function to map a range of values to another
def map_range(value, leftMin, leftMax, rightMin, rightMax):
# Figure out how 'wide' each range is
leftSpan = leftMax - leftMin
rightSpan = rightMax - rightMin
# Convert the left range into a 0-1 range (float)
valueScaled = float(value - leftMin) / float(leftSpan)
# Convert the 0-1 range into a value in the right range.
return rightMin + (valueScaled * rightSpan)
# Player class
class Player:
def __init__(self, x, y, width=64, height=64):
self.rect = pygame.Rect(x, y, width, height)
self.dirx = 0
self.diry = 0
def draw(self):
rectangle = pygame.draw.rect(screen, (255, 0, 0), self.rect)
# Target class
class Target:
def __init__(self, x, y, acceleration=0.25):
self.x, self.y = x, y
self.image = pygame.image.load(
__file__.replace(os.path.basename(__file__), "target.png")
)
self.speed = 0
self.acceleration = acceleration
def draw(self):
screen.blit(self.image, (self.x, self.y))
def update(self):
self.speed -= self.acceleration
self.x += int(self.speed)
if self.speed < -1:
self.speed = 0
player = Player(64, HEIGHT - 128)
# Targets init
targets = []
targets_spawn_time = 3000
previous_ticks = pygame.time.get_ticks()
# Ground animation init
ground_frames = []
for i in os.listdir(__file__.replace(os.path.basename(__file__), "ground_frames")):
ground_frames.append(
pygame.image.load(
__file__.replace(os.path.basename(__file__), "ground_frames\\" + i)
)
) # Load all ground frames
ground_frame_counter = 0 # Keep track of the current ground frame
frame_counter = 0
# Bow
bow = pygame.image.load(__file__.replace(os.path.basename(__file__), "bow.png"))
angle = 0
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# Spawning the targets
current_ticks = pygame.time.get_ticks()
if current_ticks - previous_ticks >= targets_spawn_time:
targets.append(Target(WIDTH, randint(0, HEIGHT - 110)))
previous_ticks = current_ticks
screen.fill((101, 203, 214))
player.draw()
for i, e in list(enumerate(targets))[::-1]:
e.draw()
e.update()
if e.x <= -e.image.get_rect().width:
del targets[i]
# Calculating the angle of the bow
mouse_pos = pygame.Vector2(pygame.mouse.get_pos())
angle = map_range(mouse_pos.x, 0, WIDTH, 90, 0)
# Rotate the bow
rotated_bow, rotated_bow_rect = rot_from_zero(bow, angle)
rotated_bow_rect.center = player.rect.center
screen.blit(rotated_bow, rotated_bow_rect)
# Animate the ground
if frame_counter % 24 == 0:
ground_frame_counter += 1
if ground_frame_counter >= len(ground_frames):
ground_frame_counter = 0
for i in range(round(WIDTH / ground_frames[ground_frame_counter].get_rect().width)):
screen.blit(
ground_frames[ground_frame_counter],
(
ground_frames[ground_frame_counter].get_rect().width * i,
HEIGHT - ground_frames[ground_frame_counter].get_rect().height,
),
)
# Calculating the trajectory
mouse_pos.x = (
mouse_pos.x if mouse_pos.x != rotated_bow_rect.centerx else mouse_pos.x + 1
)
# print(mouse_pos, rotated_bow_rect.center)
v_x = rotated_bow_rect.centerx + ((mouse_pos.x - rotated_bow_rect.centerx) / 2)
trajectory_parabola = parabola.Parabola(
1,
rotated_bow_rect.center,
(mouse_pos.x, rotated_bow_rect.centery),
(v_x, mouse_pos.y),
)
trajectory = [(i[0], int(i[1])) for i in trajectory_parabola.punti(0, WIDTH)]
pygame.draw.lines(screen, (0, 0, 0), False, trajectory)
pygame.draw.ellipse(
screen, (128, 128, 128), pygame.Rect(v_x - 15, mouse_pos.y - 15, 30, 30)
)
pygame.draw.ellipse(
screen,
(128, 128, 128),
pygame.Rect(mouse_pos.x - 15, rotated_bow_rect.centery - 15, 30, 30),
)
pygame.display.update()
if frame_counter == 120:
for i in trajectory:
print(i)
frame_counter += 1
You can run all of this and understand what's wrong with it, help?
You round the values of a, b and c to 2 decimal places. This is too inaccurate for this application:
self.__a = round(matrix_c[0], 2)
self.__b = round(matrix_c[1], 2)
self.__c = round(matrix_c[2], 2)
self.__a = matrix_c[0]
self.__b = matrix_c[1]
self.__c = matrix_c[2]
Similar to answer above... rounding is the issue here. This is magnified when the scale of the coordinates gets bigger.
However, disagree with other solution: It does not matter what order you pass the coordinates into your parabola construction. Any order works fine. points are points.
Here is a pic of your original parabola function "drooping" because of rounding error:
p1 = (0, 10) # left
p2 = (100, 10) # right
p3 = (50, 100) # apex
p = Parabola(1, p3, p2, p1)
traj = p.punti(0, 100)
xs, ys = zip(*traj)
plt.scatter(xs, ys)
plt.plot([0, 100], [10, 10], color='r')
plt.show()
I'm trying to create a rotating plane in pygame. I'm converting 3d (x, y, z) coordinates to screen coordinates (x, y), and then rotating the screen coordinates. This seems to work when its rotating on both the x and y axis, but when it's rotating on only one axis (I commented out the y axis rotation) it is slanted. I can't seem to figure out why?
import pygame
import math
red = (255, 0, 0)
class Vector3:
def __init__(self, _x, _y, _z):
self.x = _x
self.y = _y
self.z = _z
class Vector2:
def __init__(self, _x, _y):
self.x = _x
self.y = _y
class Plane:
def draw(self, screen, value):
scale = 25
points = []
vertices = [Vector3(0, 1, 0),
Vector3(1, 1, 0),
Vector3(1, 0, 0),
Vector3(0, 0, 0)]
for vert in vertices:
x, y = vec3to2(vert)
points.append(Vector2(x * scale + 40, y * scale + 100))
print((x, y))
centerx = (points[0].x + points[1].x + points[2].x + points[3].x) / 4
centery = (points[0].y + points[1].y + points[2].y + points[3].y) / 4
for point in points:
rotx, roty = vec3rot(point, math.radians(value), centerx, centery)
point.x = rotx
#point.y = roty
pygame.draw.line(screen, red, (points[0].x, points[0].y), (points[1].x, points[1].y))
pygame.draw.line(screen, red, (points[1].x, points[1].y), (points[2].x, points[2].y))
pygame.draw.line(screen, red, (points[0].x, points[0].y), (points[3].x, points[3].y))
pygame.draw.line(screen, red, (points[3].x, points[3].y), (points[2].x, points[2].y))
pygame.draw.circle(screen, red, (int(centerx), int(centery)), 1)
def vec3to2(vect3):
try:
_x = vect3.x / vect3.z
except ZeroDivisionError:
_x = vect3.x
try:
_y = vect3.y / vect3.z
except ZeroDivisionError:
_y = vect3.y
return(_x, _y)
def vec3rot(vect3, theta, centerx, centery):
_x = centerx + (vect3.x - centerx) * math.cos(theta) - (vect3.y - centery) * math.sin(theta)
_y = centery + (vect3.x - centerx) * math.sin(theta) + (vect3.y - centery) * math.cos(theta)
return(_x, _y)
def main():
pygame.init()
screen = pygame.display.set_mode((640, 480))
v = 0
plane = Plane()
running = True
while running:
screen.fill((0, 0, 0))
plane.draw(screen, v)
pygame.display.flip()
v += 0.1
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
main()
I was modifying the following code to track an object and to save the image of the object being tracked:
#!/usr/bin/env python
'''
MOSSE tracking sample
This sample implements correlation-based tracking approach, described in [1].
Usage:
mosse.py [--pause] [<video source>]
--pause - Start with playback paused at the first video frame.
Useful for tracking target selection.
Draw rectangles around objects with a mouse to track them.
Keys:
SPACE - pause video
c - clear targets
[1] David S. Bolme et al. "Visual Object Tracking using Adaptive Correlation Filters"
http://www.cs.colostate.edu/~bolme/publications/Bolme2010Tracking.pdf
'''
# Python 2/3 compatibility
from __future__ import print_function
import sys
PY3 = sys.version_info[0] == 3
if PY3:
xrange = range
import numpy as np
import cv2
from common import draw_str, RectSelector
import video
def rnd_warp(a):
h, w = a.shape[:2]
T = np.zeros((2, 3))
coef = 0.2
ang = (np.random.rand()-0.5)*coef
c, s = np.cos(ang), np.sin(ang)
T[:2, :2] = [[c,-s], [s, c]]
T[:2, :2] += (np.random.rand(2, 2) - 0.5)*coef
c = (w/2, h/2)
T[:,2] = c - np.dot(T[:2, :2], c)
return cv2.warpAffine(a, T, (w, h), borderMode = cv2.BORDER_REFLECT)
def divSpec(A, B):
Ar, Ai = A[...,0], A[...,1]
Br, Bi = B[...,0], B[...,1]
C = (Ar+1j*Ai)/(Br+1j*Bi)
C = np.dstack([np.real(C), np.imag(C)]).copy()
return C
eps = 1e-5
class MOSSE:
def __init__(self, frame, rect):
x1, y1, x2, y2 = rect
w, h = map(cv2.getOptimalDFTSize, [x2-x1, y2-y1])
x1, y1 = (x1+x2-w)//2, (y1+y2-h)//2
self.pos = x, y = x1+0.5*(w-1), y1+0.5*(h-1)
self.size = w, h
img = cv2.getRectSubPix(frame, (w, h), (x, y))
self.win = cv2.createHanningWindow((w, h), cv2.CV_32F)
g = np.zeros((h, w), np.float32)
g[h//2, w//2] = 1
g = cv2.GaussianBlur(g, (-1, -1), 2.0)
g /= g.max()
self.G = cv2.dft(g, flags=cv2.DFT_COMPLEX_OUTPUT)
self.H1 = np.zeros_like(self.G)
self.H2 = np.zeros_like(self.G)
for i in xrange(128):
a = self.preprocess(rnd_warp(img))
A = cv2.dft(a, flags=cv2.DFT_COMPLEX_OUTPUT)
self.H1 += cv2.mulSpectrums(self.G, A, 0, conjB=True)
self.H2 += cv2.mulSpectrums( A, A, 0, conjB=True)
self.update_kernel()
self.update(frame)
def update(self, frame, rate = 0.125):
(x, y), (w, h) = self.pos, self.size
self.last_img = img = cv2.getRectSubPix(frame, (w, h), (x, y))
img = self.preprocess(img)
self.last_resp, (dx, dy), self.psr = self.correlate(img)
self.good = self.psr > 8.0
if not self.good:
return
self.pos = x+dx, y+dy
self.last_img = img = cv2.getRectSubPix(frame, (w, h), self.pos)
img = self.preprocess(img)
A = cv2.dft(img, flags=cv2.DFT_COMPLEX_OUTPUT)
H1 = cv2.mulSpectrums(self.G, A, 0, conjB=True)
H2 = cv2.mulSpectrums( A, A, 0, conjB=True)
self.H1 = self.H1 * (1.0-rate) + H1 * rate
self.H2 = self.H2 * (1.0-rate) + H2 * rate
self.update_kernel()
#property
def state_vis(self):
f = cv2.idft(self.H, flags=cv2.DFT_SCALE | cv2.DFT_REAL_OUTPUT )
h, w = f.shape
f = np.roll(f, -h//2, 0)
f = np.roll(f, -w//2, 1)
kernel = np.uint8( (f-f.min()) / f.ptp()*255 )
resp = self.last_resp
resp = np.uint8(np.clip(resp/resp.max(), 0, 1)*255)
vis = np.hstack([self.last_img, kernel, resp])
return vis
def draw_state(self, vis):
(x, y), (w, h) = self.pos, self.size
x1, y1, x2, y2 = int(x-0.5*w), int(y-0.5*h), int(x+0.5*w), int(y+0.5*h)
cv2.rectangle(vis, (x1, y1), (x2, y2), (0, 0, 255))
if self.good:
cv2.circle(vis, (int(x), int(y)), 2, (0, 0, 255), -1)
else:
cv2.line(vis, (x1, y1), (x2, y2), (0, 0, 255))
cv2.line(vis, (x2, y1), (x1, y2), (0, 0, 255))
draw_str(vis, (x1, y2+16), 'PSR: %.2f' % self.psr)
def preprocess(self, img):
img = np.log(np.float32(img)+1.0)
img = (img-img.mean()) / (img.std()+eps)
return img*self.win
def correlate(self, img):
C = cv2.mulSpectrums(cv2.dft(img, flags=cv2.DFT_COMPLEX_OUTPUT), self.H, 0, conjB=True)
resp = cv2.idft(C, flags=cv2.DFT_SCALE | cv2.DFT_REAL_OUTPUT)
h, w = resp.shape
_, mval, _, (mx, my) = cv2.minMaxLoc(resp)
side_resp = resp.copy()
cv2.rectangle(side_resp, (mx-5, my-5), (mx+5, my+5), 0, -1)
smean, sstd = side_resp.mean(), side_resp.std()
psr = (mval-smean) / (sstd+eps)
return resp, (mx-w//2, my-h//2), psr
def update_kernel(self):
self.H = divSpec(self.H1, self.H2)
self.H[...,1] *= -1
class App:
def __init__(self, video_src, paused = False):
self.cap = video.create_capture(video_src)
_, self.frame = self.cap.read()
cv2.imshow('frame', self.frame)
self.rect_sel = RectSelector('frame', self.onrect)
self.trackers = []
self.paused = paused
def onrect(self, rect):
frame_gray = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY)
tracker = MOSSE(frame_gray, rect)
self.trackers.append(tracker)
def run(self):
while True:
if not self.paused:
ret, self.frame = self.cap.read()
if not ret:
break
frame_gray = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY)
for tracker in self.trackers:
tracker.update(frame_gray)
vis = self.frame.copy()
for tracker in self.trackers:
tracker.draw_state(vis)
if len(self.trackers) > 0:
cv2.imshow('tracker state', self.trackers[-1].state_vis)
cv2.imwrite('object.jpg', self.trackers[-1].state_vis)
self.rect_sel.draw(vis)
cv2.imshow('frame', vis)
ch = cv2.waitKey(10) & 0xFF
if ch == 27:
break
if ch == ord(' '):
self.paused = not self.paused
if ch == ord('c'):
self.trackers = []
if __name__ == '__main__':
print (__doc__)
import sys, getopt
opts, args = getopt.getopt(sys.argv[1:], '', ['pause'])
opts = dict(opts)
try:
video_src = args[0]
except:
video_src = '0'
App(video_src, paused = '--pause' in opts).run()
But in the tracker state window it captures the contours and the embossed version of the tracked object (which I don't need) along with the object.I just need a normal image of the object(the first section of the tracker state window).
Note that there are three sections in the tracker state window.
Could anyone help?
Thanks!
Based on this video of the Mosse tracking, I assume that the contours of the object are in a rectangular shape, which is obvious when taking a look at the code.
I don't have a straight forward answer ( dislike people posting homework here) but still, I have a tip :
try commenting the lines containing cv2.rectangle() (link to doc) as it's the responsible of drawing rectangles (as you may have guessed).
That shouldn't be too hard to solve !