Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am trying to implement a fairly basic boids simulation in python. My goal is to have a simulation with a basic predator prey setup. I found some pseudocode (can't post more than two links but it is the first result if you google boids pseudocode) and some code here and decided to give it a go. Because I wanted to add predators, I decided to give take the code I found modify it so that the boids (that will become prey) are sprites, and then go from there. However, I have run into this problem.
After I modified the code to use pygame sprites, all of the boids move to the lower right hand corner (the original code worked correctly).
My code (just clone the repo) is here(github). Has anyone ever run into the first issue? Does anyone have any ideas to solve it? As for question 2, could someone please explain how to do that?
Thank you and any help would be greatly appreciated.
P.S.
The behavior of the boids (their movement) appears to be working fine apart from the fact that they always go to the lower right hand corner.
P.P.S.
Thanks to furas the prey behave correctly now.
P.P.P.S.
As the debugging problem has been solved, the part of my question that remains involves an explanation, and I think should be on topic.
You have to differences in your code
You use different velocity speed at start - in __init__ but this shouldn't do difference.
You updates object in different moment.
You move all preys (using Group update) at the same time - after all calculations.
Original code moves every boid after its calculations so next boid use different data to calculate its move.
I put prey.update() inside for loops and remove all_sprites_list.update()
I organize code in a little different:
#!/usr/bin/env python
# Prey implementation in Python using PyGame
from __future__ import division # required in Python 2.7
import sys
import pygame
import random
import math
# === constants === (UPPER_CASE names)
SCREEN_SIZE = SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
BLACK = (0, 0, 0)
MAX_VELOCITY = 10
NUM_BOIDS = 50
BORDER = 25
# === classes === (CamelCase names for classes / lower_case names for method)
class Prey(pygame.sprite.Sprite):
def __init__(self, x, y):
super(Prey, self).__init__()
# Load image as sprite
self.image = pygame.image.load("ressources/img/prey.png").convert()
# Fetch the rectangle object that has the dimensions of the image
self.rect = self.image.get_rect()
# Coordinates
self.rect.x = x
self.rect.y = y
# self.velocityX = random.randint(-10, 10) / 10.0
# self.velocityY = random.randint(-10, 10) / 10.0
self.velocityX = random.randint(1, 10) / 10.0
self.velocityY = random.randint(1, 10) / 10.0
def distance(self, prey):
'''Return the distance from another prey'''
distX = self.rect.x - prey.rect.x
distY = self.rect.y - prey.rect.y
return math.sqrt(distX * distX + distY * distY)
def move_closer(self, prey_list):
'''Move closer to a set of prey_list'''
if len(prey_list) < 1:
return
# calculate the average distances from the other prey_list
avgX = 0
avgY = 0
for prey in prey_list:
if prey.rect.x == self.rect.x and prey.rect.y == self.rect.y:
continue
avgX += (self.rect.x - prey.rect.x)
avgY += (self.rect.y - prey.rect.y)
avgX /= len(prey_list)
avgY /= len(prey_list)
# set our velocity towards the others
distance = math.sqrt((avgX * avgX) + (avgY * avgY)) * -1.0
self.velocityX -= (avgX / 100)
self.velocityY -= (avgY / 100)
def move_with(self, prey_list):
'''Move with a set of prey_list'''
if len(prey_list) < 1:
return
# calculate the average velocities of the other prey_list
avgX = 0
avgY = 0
for prey in prey_list:
avgX += prey.velocityX
avgY += prey.velocityY
avgX /= len(prey_list)
avgY /= len(prey_list)
# set our velocity towards the others
self.velocityX += (avgX / 40)
self.velocityY += (avgY / 40)
def move_away(self, prey_list, minDistance):
'''Move away from a set of prey_list. This avoids crowding'''
if len(prey_list) < 1:
return
distanceX = 0
distanceY = 0
numClose = 0
for prey in prey_list:
distance = self.distance(prey)
if distance < minDistance:
numClose += 1
xdiff = (self.rect.x - prey.rect.x)
ydiff = (self.rect.y - prey.rect.y)
if xdiff >= 0:
xdiff = math.sqrt(minDistance) - xdiff
elif xdiff < 0:
xdiff = -math.sqrt(minDistance) - xdiff
if ydiff >= 0:
ydiff = math.sqrt(minDistance) - ydiff
elif ydiff < 0:
ydiff = -math.sqrt(minDistance) - ydiff
distanceX += xdiff
distanceY += ydiff
if numClose == 0:
return
self.velocityX -= distanceX / 5
self.velocityY -= distanceY / 5
def update(self):
'''Perform actual movement based on our velocity'''
if abs(self.velocityX) > MAX_VELOCITY or abs(self.velocityY) > MAX_VELOCITY:
scaleFactor = MAX_VELOCITY / max(abs(self.velocityX), abs(self.velocityY))
self.velocityX *= scaleFactor
self.velocityY *= scaleFactor
self.rect.x += self.velocityX
self.rect.y += self.velocityY
# === main === (lower_case names)
# --- init ---
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
#screen_rect = screen.get_rect()
# --- objects ---
# lists
# This is a list of 'sprites.' Each block in the program is
# added to this list. The list is managed by a class called 'Group.'
prey_list = pygame.sprite.Group()
# This is a list of every sprite. All blocks and the player block as well.
all_sprites_list = pygame.sprite.Group()
# create prey_list at random positions
for i in range(NUM_BOIDS):
prey = Prey(random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT))
# Add the prey to the list of objects
prey_list.add(prey)
all_sprites_list.add(prey)
# --- mainloop ---
clock = pygame.time.Clock()
running = True
while running:
# --- events ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
# --- updates ---
for prey in prey_list:
closeBoids = []
for otherBoid in prey_list:
if otherBoid == prey:
continue
distance = prey.distance(otherBoid)
if distance < 200:
closeBoids.append(otherBoid)
prey.move_closer(closeBoids)
prey.move_with(closeBoids)
prey.move_away(closeBoids, 20)
# ensure they stay within the screen space
# if we roubound we can lose some of our velocity
if prey.rect.x < BORDER and prey.velocityX < 0:
prey.velocityX = -prey.velocityX * random.random()
if prey.rect.x > SCREEN_WIDTH - BORDER and prey.velocityX > 0:
prey.velocityX = -prey.velocityX * random.random()
if prey.rect.y < BORDER and prey.velocityY < 0:
prey.velocityY = -prey.velocityY * random.random()
if prey.rect.y > SCREEN_HEIGHT - BORDER and prey.velocityY > 0:
prey.velocityY = -prey.velocityY * random.random()
prey.update()
# Calls update() method on every sprite in the list
#all_sprites_list.update()
# --- draws ---
screen.fill(BLACK)
# Draw all the spites
all_sprites_list.draw(screen)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 60 frames per second
# Used to manage how fast the screen updates
clock.tick(120)
# --- the end ---
pygame.quit()
sys.exit()
EDIT: real problem was dividing of two integer numbers in Python 2 which gives result rounded to integer number
Solution:
from __future__ import division
Use this before other imports.
Related
i am working on a project of swarm algorithms and i am trying to make complex shapes using the swarm consensus. However, the mathematics to achieve that seems quite difficult for me.
I have been able to make shapes like stars, circle and triangle but to make other complex shapes seems more harder. It would be very helpful if i get the idea of using numpy arrays to build these complex shapes using swarms....................................................
# general function to reset radian angle to [-pi, pi)
def reset_radian(radian):
while radian >= math.pi:
radian = radian - 2*math.pi
while radian < -math.pi:
radian = radian + 2*math.pi
return radian
# general function to calculate next position node along a heading direction
def cal_next_node(node_poses, index_curr, heading_angle, rep_times):
for _ in range(rep_times):
index_next = index_curr + 1
x = node_poses[index_curr][0] + 1.0*math.cos(heading_angle)
y = node_poses[index_curr][1] + 1.0*math.sin(heading_angle)
node_poses[index_next] = np.array([x,y])
index_curr = index_next
return index_next
##### script to generate star #####
filename = 'star'
swarm_size = 30
node_poses = np.zeros((swarm_size, 2))
outer_angle = 2*math.pi / 5.0
devia_right = outer_angle
devia_left = 2*outer_angle
# first node is at bottom left corner
heading_angle = outer_angle / 2.0 # current heading
heading_dir = 0 # current heading direction: 0 for left, 1 for right
seg_count = 0 # current segment count
for i in range(1,swarm_size):
node_poses[i] = (node_poses[i-1] +
np.array([math.cos(heading_angle), math.sin(heading_angle)]))
seg_count = seg_count + 1
if seg_count == 3:
seg_count = 0
if heading_dir == 0:
heading_angle = reset_radian(heading_angle - devia_right)
heading_dir = 1
else:
heading_angle = reset_radian(heading_angle + devia_left)
heading_dir = 0
print(node_poses)
with open(filename, 'w') as f:
pickle.dump(node_poses, f)
pygame.init()
# find the right world and screen sizes
x_max, y_max = np.max(node_poses, axis=0)
x_min, y_min = np.min(node_poses, axis=0)
pixel_per_length = 30
world_size = (x_max - x_min + 2.0, y_max - y_min + 2.0)
screen_size = (int(world_size[0])*pixel_per_length, int(world_size[1])*pixel_per_length)
# convert node poses in the world to disp poses on screen
def cal_disp_poses():
poses_temp = np.zeros((swarm_size, 2))
# shift the loop to the middle of the world
middle = np.array([(x_max+x_min)/2.0, (y_max+y_min)/2.0])
for i in range(swarm_size):
poses_temp[i] = (node_poses[i] - middle +
np.array([world_size[0]/2.0, world_size[1]/2.0]))
# convert to display coordinates
poses_temp[:,0] = poses_temp[:,0] / world_size[0]
poses_temp[:,0] = poses_temp[:,0] * screen_size[0]
poses_temp[:,1] = poses_temp[:,1] / world_size[1]
poses_temp[:,1] = 1.0 - poses_temp[:,1]
poses_temp[:,1] = poses_temp[:,1] * screen_size[1]
return poses_temp.astype(int)
disp_poses = cal_disp_poses()
# draw the loop shape on pygame window
color_white = (255,255,255)
color_black = (0,0,0)
screen = pygame.display.set_mode(screen_size)
screen.fill(color_white)
for i in range(swarm_size):
pygame.draw.circle(screen, color_black, disp_poses[i], 5, 0)
for i in range(swarm_size-1):
pygame.draw.line(screen, color_black, disp_poses[i], disp_poses[i+1],2)
pygame.draw.line(screen, color_black, disp_poses[0], disp_poses[swarm_size-1], 2)
pygame.display.update()
Your method for drawing takes huge advantage of the symmetries in the shapes you are drawing. More complex shapes will have fewer symmetries and so your method will require a lot of tedious work to get them drawn with stars. Without symmetry you may be better served writing each individual line 'command' in a list and following that list. For example, drawing the number 4 starting from the bottom (assuming 0 degrees is --> that way):
angles = [90,225,0]
distances = [20,15,12]
Then with a similar program to what you have, you can start drawing dots in a line at 90 degrees for 20 dots, then 225 degrees for 15 dots etc... Then by adding to these two lists you can build up a very complicated shape without relying on symmetry.
My question is connected to this question:
rotating gun with restricted movement
What calculations do you need to do to get bullets firing from the gun muzzle?
my code is given below
def gun_radar(self):
for p in self.gameobjects:
if "R" in p.name or "L" in p.name:
if abs(p.rect.centerx - self.hero.rect.centerx) < p.radar and abs(p.rect.centery - self.hero.rect.centery) < p.radar: # if hero within radar
p.vec_to_target = pygame.math.Vector2(self.hero.rect.center) - p.rect.center
p.direction = p.orig_direction.rotate(p.current_angle)
p.orientation = p.vec_to_target.dot(p.direction)
if p.orientation > 2:
p.current_angle += 1
elif p.orientation < -2:
p.current_angle -= 1
p.current_angle = p.clamp(p.current_angle, p.clamp_min, p.clamp_max)
p.gun_rotate(-p.current_angle)
self.blt_timer -= 1 #count down the timer. when zero calculate vector and add bullet to fired_blts
if self.blt_timer<= 0:
w, h = p.rect.center
angle_in_rad = p.current_angle * (math.pi) / 180
w = w + math.cos(angle_in_rad)
h = h + math.sin(-angle_in_rad)
bullet = Bombullet(bulletimage, w, h)
bullet.xvel = bullet.speed * math.cos(angle_in_rad)
bullet.yvel = bullet.speed * math.sin(angle_in_rad)
bullet.rect.x += bullet.xvel
bullet.rect.y += bullet.yvel
self.fired_blts.add(bullet)
self.blt_timer = 100
when the hero comes within a circular area of the gun it is activated and a bullet shoots from the center of the gun.
I move the bullet with
def move(self):
self.rect.x += self.xvel
self.rect.y += self.yvel
print self.rect.x, self.rect.y, self.life
self.life -= 1
the bullet updates and shoots in the correct direction but shoots from the center of the gun. How do I move the shooting point to the muzzle?
Here's an example of a firing cannon. You just have to pass the current angle and the rect.center of the cannon to the newly created bullet and then rotate its image and velocity vector in the __init__ method.
import math
import pygame as pg
from pygame.math import Vector2
pg.init()
screen = pg.display.set_mode((640, 480))
FONT = pg.font.Font(None, 24)
BLACK = pg.Color('black')
BG_COLOR = pg.Color('darkseagreen4')
class Bullet(pg.sprite.Sprite):
def __init__(self, pos, angle):
super(Bullet, self).__init__()
self.image = pg.Surface((20, 11), pg.SRCALPHA)
pg.draw.rect(self.image, pg.Color('grey11'), [0, 0, 13, 11])
pg.draw.polygon(
self.image, pg.Color('grey11'), [(13, 0), (20, 5), (13, 10)])
self.image = pg.transform.rotate(self.image, -angle)
self.rect = self.image.get_rect(center=pos)
# To apply an offset to the start position,
# create another vector and rotate it as well.
offset = Vector2(80, 0).rotate(angle)
# Use the offset to change the starting position.
self.pos = Vector2(pos) + offset
self.velocity = Vector2(5, 0)
self.velocity.rotate_ip(angle)
def update(self):
self.pos += self.velocity
self.rect.center = self.pos
def main():
clock = pg.time.Clock()
# The cannon image and rect.
surf = pg.Surface((40, 22), pg.SRCALPHA)
surf.fill(pg.Color('grey27'))
pg.draw.rect(surf, pg.Color('grey11'), [30, 6, 10, 10])
orig_surf = surf
rect = surf.get_rect(center=(320, 240))
angle = 0 # Angle of the cannon.
# Add bullets to this group.
bullet_group = pg.sprite.Group()
playing = True
while playing:
for event in pg.event.get():
if event.type == pg.QUIT:
playing = False
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1: # Left button fires bullet.
# Fire a bullet from cannon center with current angle.
bullet_group.add(Bullet(rect.center, angle))
bullet_group.update()
# Find angle to target (mouse pos).
x, y = Vector2(pg.mouse.get_pos()) - rect.center
angle = math.degrees(math.atan2(y, x))
# Rotate the cannon image.
surf = pg.transform.rotate(orig_surf, -angle)
rect = surf.get_rect(center=rect.center)
# Draw
screen.fill(BG_COLOR)
bullet_group.draw(screen)
screen.blit(surf, rect)
txt = FONT.render('angle {:.1f}'.format(angle), True, BLACK)
screen.blit(txt, (10, 10))
pg.draw.line(
screen, pg.Color(150, 60, 20), rect.center, pg.mouse.get_pos(), 2)
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
You can also use math.cos and sin to calculate the offset.
run = math.cos(math.radians(angle)) * 80
rise = math.sin(math.radians(angle)) * 80
offset = run, rise
I achieved what I needed to do with thefollowing code which is the same as above with a very slight modification which is explained in the code. #skrx answer and the comment by Nick A. aided me to achieve this.
def gun_radar(self):
for p in self.gameobjects:
if "R" in p.name or "L" in p.name:
if abs(p.rect.centerx - self.hero.rect.centerx) < p.radar and abs(p.rect.centery - self.hero.rect.centery) < p.radar: # if hero within radar
p.vec_to_target = pygame.math.Vector2(self.hero.rect.center) - p.rect.center
p.direction = p.orig_direction.rotate(p.current_angle)
p.orientation = p.vec_to_target.dot(p.direction)
if p.orientation > 2:
p.current_angle += 1
elif p.orientation < -2:
p.current_angle -= 1
p.current_angle = p.clamp(p.current_angle, p.clamp_min, p.clamp_max)
p.gun_rotate(-p.current_angle)
p.timer -= 1 #count down the timer. when zero calculate vector and add bullet to fired_blts
if p.timer<= 0:
w, h = p.rect.center
# adjust for the distance fromm the gun center to the gun muzzle
w = w + math.cos(math.radians(p.current_angle)) * 28
h = h + math.sin(math.radians(p.current_angle)) * 28
bullet = Bombullet(bulletimage, w, h) # create the bullet
# calculate the velocity
bullet.xvel = bullet.speed * math.cos(math.radians(p.current_angle))
bullet.yvel = bullet.speed * math.sin(math.radians(p.current_angle))
self.fired_blts.add(bullet)
p.timer = 100
My question is related to the answer of the question below.
rotating a rectangle.
How do i restrict the rotation to the area marked in red as in this picture? I tried various ways but was unsuccesfull. The code always keeps the poiter in the other area.
my modified code below.
import pygame, math, base64
pygame.init()
screen = pygame.display.set_mode((200, 200))
surf = pygame.image.load("D:\\PYTHON\\SoftwareDG\\Games\\Platform_Suvivor\\assets\\rg.png").convert_alpha()
def rot_center(image, angle):
orig_rect = image.get_rect()
rot_image = pygame.transform.rotate(image, angle)
rot_rect = orig_rect.copy()
rot_rect.center = rot_image.get_rect().center
rot_image = rot_image.subsurface(rot_rect).copy()
return rot_image
current_angle = 0
while True:
if pygame.event.get(pygame.QUIT): break
pygame.event.get()
mouseX, mouseY = pygame.mouse.get_pos()
rect = surf.get_rect(center=(92, 92))
target_angle = math.degrees(math.atan2(mouseY - rect.centery, mouseX - rect.centerx))
if target_angle < -120:
target_angle = -120
if target_angle > 120:
target_angle = 120
print target_angle
if current_angle > target_angle:
current_angle -= 0.03
if current_angle < target_angle:
current_angle += 0.03
screen.fill((40, 140, 40))
screen.blit(rot_center(surf, -current_angle), rect)
pygame.display.update()
Are you looking for a clamp function like this?
def clamp(value, min_, max_):
"""Clamp value to a range between min_ and max_."""
return max(min_, min(value, max_))
In your case you also have to check if the current_angle is greater or less than 0.
if current_angle <= 0:
current_angle = clamp(current_angle, -180, -120)
elif current_angle > 0:
current_angle = clamp(current_angle, 120, 180)
Update: Here's the example with vectors. I use the vectors to figure out in which direction the sprite needs to be rotated. Note that right is now 0 degrees, left is 180° and it goes from 0° to 360°.
And here are some interesting links that helped me to learn how to do this:
http://docs.godotengine.org/en/stable/tutorials/matrices_and_transforms.html
http://www.wildbunny.co.uk/blog/vector-maths-a-primer-for-games-programmers/
import math
import pygame
pygame.init()
screen = pygame.display.set_mode((300, 300))
font = pygame.font.Font(None, 24)
GRAY = pygame.Color('gray90')
def clamp(value, min_value, max_value):
"""Clamp value to a range between min_value and max_value."""
return max(min_value, min(value, max_value))
def main():
current_angle = 0
clock = pygame.time.Clock()
surf = pygame.Surface((80, 50), pygame.SRCALPHA)
pygame.draw.polygon(surf, (40, 100, 200), ((0, 0), (80, 25), (0, 50)))
orig_surf = surf
rect = surf.get_rect(center=(150, 150))
orig_direction = pygame.math.Vector2(0, 1)
playing = True
while playing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
playing = False
# Here I figure out if the target is closer in clock-
# or counterclockwise direction. `orientation` is positive
# if the target is closer in clockwise and negative
# if it's in counterclockwise direction.
vec_to_target = pygame.math.Vector2(pygame.mouse.get_pos()) - rect.center
direction = orig_direction.rotate(current_angle)
orientation = vec_to_target.dot(direction)
# I use orientation > 3 and < -3 instead of 0 to
# avoid jittering when the target angle is reached.
if orientation > 3:
current_angle += 3
elif orientation < -3:
current_angle -= 3
# You can use this modulo operation to keep the angle between
# 0° and 360°, but this is not needed because of the clamp.
# current_angle %= 360
# Clamp the value to the desired range.
current_angle = clamp(current_angle, 120, 240)
surf = pygame.transform.rotate(orig_surf, -current_angle)
rect = surf.get_rect(center=rect.center)
# Draw
screen.fill((40, 40, 40))
screen.blit(surf, rect)
txt = font.render('angle {:.1f}'.format(current_angle), True, GRAY)
screen.blit(txt, (10, 10))
txt = font.render('orientation {:.1f}'.format(orientation), True, GRAY)
screen.blit(txt, (10, 25))
pygame.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
pygame.quit()
I managed to restrict the rotation of the gun to a limited area by just adding a one line check just before rotation. The code is far from perfect. When crossing from the + quadrant to the - quadrant and vice versa, the gun rotates in the opposite direction to get to the other side. More checks before incrementing the angle will correct the direction of rotation but for now the problem is solved in a basic way. The code is given below.
import pygame, math, base64
pygame.init()
screen = pygame.display.set_mode((200, 200))
surf = pygame.image.load("put your image here").convert_alpha()
def rot_center(image, angle):
orig_rect = image.get_rect()
rot_image = pygame.transform.rotate(image, angle)
rot_rect = orig_rect.copy()
rot_rect.center = rot_image.get_rect().center
rot_image = rot_image.subsurface(rot_rect).copy()
return rot_image
current_angle = -180
clock = pygame.time.Clock()
while True:
if pygame.event.get(pygame.QUIT): break
pygame.event.get()
mouseX, mouseY = pygame.mouse.get_pos()
rect = surf.get_rect(center=(92, 92))
target_angle = math.degrees(math.atan2(mouseY - rect.centery, mouseX - rect.centerx))
# the new line is just below
if 180 > target_angle > 120 or - 120 > target_angle > -179:
print "ok", target_angle
if current_angle > target_angle:
current_angle -= 2
if current_angle < target_angle:
current_angle += 2
screen.fill((40, 140, 40))
screen.blit(rot_center(surf, -current_angle), rect)
pygame.display.update()
clock.tick(60)
I am working in vehicle counting with opencv and python programming, I already complete step:
1. Detect moving vehicle with BackgroundSubtractorMOG2
2. Draw rectangle on it, then poin a centroid of that
3. Draw a line (to indicate of the counting)
if that centroid accros/intercept with the line I want count that 1. but in my code sometime it add sometime no. Here the line code:
cv2.line(frame,(0,170),(300,170),(200,200,0),2)
and there the centroid:
if w > 20 and h > 25:
cv2.rectangle(frame, (x,y), (x+w,y+h), (180, 1, 0), 1)
x1=w/2
y1=h/2
cx=x+x1
cy=y+y1
centroid=(cx,cy)
cv2.circle(frame,(int(cx),int(cy)),4,(0,255,0),-1)
my counting code:
if cy==170:
counter=counter+1
Can anyone help me. please. for your advice thankyou!
Here is my approach that would work independently of the video frame rate. Assuming that you are able to track a car's centroid at each frame, I would save the last two centroids' position (last_centroid and centroid in my code) and process as follows:
compute the intercepting line equation's parameters ( (a,b,c) from aX + bY + c = 0)
compute the equation's parameters of the segment line between last_centroid and centroid
find if the two lines are intersecting
if so, increment your counter
Here is how I implemented it in OpenCV (Python):
import cv2
import numpy as np
import collections
Params = collections.namedtuple('Params', ['a','b','c']) #to store equation of a line
def calcParams(point1, point2): #line's equation Params computation
if point2[1] - point1[1] == 0:
a = 0
b = -1.0
elif point2[0] - point1[0] == 0:
a = -1.0
b = 0
else:
a = (point2[1] - point1[1]) / (point2[0] - point1[0])
b = -1.0
c = (-a * point1[0]) - b * point1[1]
return Params(a,b,c)
def areLinesIntersecting(params1, params2, point1, point2):
det = params1.a * params2.b - params2.a * params1.b
if det == 0:
return False #lines are parallel
else:
x = (params2.b * -params1.c - params1.b * -params2.c)/det
y = (params1.a * -params2.c - params2.a * -params1.c)/det
if x <= max(point1[0],point2[0]) and x >= min(point1[0],point2[0]) and y <= max(point1[1],point2[1]) and y >= min(point1[1],point2[1]):
print("intersecting in:", x,y)
cv2.circle(frame,(int(x),int(y)),4,(0,0,255), -1) #intersecting point
return True #lines are intersecting inside the line segment
else:
return False #lines are intersecting but outside of the line segment
cv2.namedWindow('frame')
frame = np.zeros((240,320,3), np.uint8)
last_centroid = (200,200) #centroid of a car at t-1
centroid = (210,180) #centroid of a car at t
line_params = calcParams(last_centroid, centroid)
intercept_line_params = calcParams((0,170), (300,170))
print("Params:", line_params.a,line_params.b,line_params.c)
while(1):
cv2.circle(frame,last_centroid,4,(0,255,0), -1) #last_centroid
cv2.circle(frame,centroid,4,(0,255,0), -1) #current centroid
cv2.line(frame,last_centroid,centroid,(0,0,255),1) #segment line between car centroid at t-1 and t
cv2.line(frame,(0,170),(300,170),(200,200,0),2) #intercepting line
print("AreLinesIntersecting: ",areLinesIntersecting(intercept_line_params,line_params,last_centroid,centroid))
cv2.imshow('frame',frame)
if cv2.waitKey(15) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
And here are some results:
Fig1. Segment is intersecting the line (intercepting line in blue - segment line between last_centroid and centroid in red)
Fig2. Segment is NOT intersecting the line
N.B. I found the formulas to calculate the intersection point here.
I hope my approach will help to address your problem.
To assume that the centroid will assume a position 170 (in x or y) is wrong, because videos generally works at 30 fps, that mean you will get 30 centroid locations per second which means even if there the object crosses the line, it may never be 170!
To counter this, one method that can be used is defining a line margin. This means now you have a line margin x before actual line (y = 170) and x after the line margin.
So if your object falls anywhere in the margin, you can increment the counter. Now the next big part would be to make a tracking mechanism wherein you collect the list of point for each object and check if it fell in the margin.
I'm using Newton's method, so I want to find the positions of all six roots of the sixth-order polynomial, basically the points where the function is zero.
I found the rough values on my graph with this code below but want to output those positions of all six roots. I'm thinking of using x as an array to input the values in to find those positions but not sure. I'm using 1.0 for now to locate the rough values. Any suggestions from here??
def P(x):
return 924*x**6 - 2772*x**5 + 3150*x**4 - 1680*x**3 + 420*x**2 - 42*x + 1
def dPdx(x):
return 5544*x**5 - 13860*x**4 + 12600*x**3 - 5040*x**2 + 840*x - 42
accuracy = 1**-10
x = 1.0
xlast = float("inf")
while np.abs(x - xlast) > accuracy:
xlast = x
x = xlast - P(xlast)/dPdx(xlast)
print(x)
p_points = []
x_points = np.linspace(0, 1, 100)
y_points = np.zeros(len(x_points))
for i in range(len(x_points)):
y_points[i] = P(x_points[i])
p_points.append(P(x_points))
plt.plot(x_points,y_points)
plt.savefig("roots.png")
plt.show()
The traditional way is to use deflation to factor out the already found roots. If you want to avoid manipulations of the coefficient array, then you have to divide the roots out.
Having found z[1],...,z[k] as root approximations, form
g(x)=(x-z[1])*(x-z[2])*...*(x-z[k])
and apply Newtons method to h(x)=f(x)/g(x) with h'(x)=f'/g-fg'/g^2. In the Newton iteration this gives
xnext = x - f(x)/( f'(x) - f(x)*g'(x)/g(x) )
Fortunately the quotient g'/g has a simple form
g'(x)/g(x) = 1/(x-z[1])+1/(x-z[2])+...+1/(x-z[k])
So with a slight modification to the Newton step you can avoid finding the same root over again.
This all still keeps the iteration real. To get at the complex root, use a complex number to start the iteration.
Proof of concept, adding eps=1e-8j to g'(x)/g(x) allows the iteration to go complex without preventing real values. Solves the equivalent problem 0=exp(-eps*x)*f(x)/g(x)
import numpy as np
import matplotlib.pyplot as plt
def P(x):
return 924*x**6 - 2772*x**5 + 3150*x**4 - 1680*x**3 + 420*x**2 - 42*x + 1
def dPdx(x):
return 5544*x**5 - 13860*x**4 + 12600*x**3 - 5040*x**2 + 840*x - 42
accuracy = 1e-10
roots = []
for k in range(6):
x = 1.0
xlast = float("inf")
x_points = np.linspace(0.0, 1.0, 200)
y_points = P(x_points)
for rt in roots:
y_points /= (x_points - rt)
y_points = np.array([ max(-1.0,min(1.0,np.real(y))) for y in y_points ])
plt.plot(x_points,y_points,x_points,0*y_points)
plt.show()
while np.abs(x - xlast) > accuracy:
xlast = x
corr = 1e-8j
for rt in roots:
corr += 1/(xlast-rt)
Px = P(xlast)
dPx = dPdx(xlast)
x = xlast - Px/(dPx - Px*corr)
print(x)
roots.append(x)