Detecting collision with sprites made of multiple pixel widths and heights - c

Context: Developing a small game on a microprocessor displayed on an LCD screen.
I'm trying to fix this collision detection function, what it does is it detects collision between a wall sprite (1 x 25 pixels) and a player sprite (3x3 pixels). It returns 1 or 0, if 1 the player sprite's dx/dy changes so it stops moving. So essentially the wall sprite is treated as a real wall.
int wall_collision(Sprite *w_sprite)
{
if (((w_sprite->x >= wall_sprite.x) && ((w_sprite->x - wall_sprite.x) < 3)) && ((w_sprite->y >= wall_sprite.y) && ((w_sprite->y - wall_sprite.y) < 3)))
return 1;
if (((w_sprite->x <= wall_sprite.x) && ((w_sprite->x - wall_sprite.x) > -3)) && ((w_sprite->y >= wall_sprite.y) && ((w_sprite->y - wall_sprite.y) < 3)))
return 1;
if (((w_sprite->x >= wall_sprite.x) && ((w_sprite->x - wall_sprite.x) < 3)) && ((w_sprite->y <= wall_sprite.y) && ((w_sprite->y - wall_sprite.y) > -3)))
return 1;
if (((w_sprite->x <= wall_sprite.x) && ((w_sprite->x - wall_sprite.x) > -3)) && ((w_sprite->y <= wall_sprite.y) && ((w_sprite->y - wall_sprite.y) > -3)))
return 1;
return 0;
}
My main issue is specifying the exact number the sprite should be equal/greater than/lesser than to, so as you can see in the example, it's 3 or -3. When I take numbers out, it returns 1 and stops the sprite regardless of where is, because the sprite is technically still on the same x or y axis as the wall, but it's not proximity wise, touching the wall. What are the correct size parameters for this?
Case problem: My sprite should only stop when it's directly touching the wall, currently it either passes through the wall, or stops when not even close to the wall.

First of all, you're code seems a little complex - below is canonical (or so I believe) method of detecting collisions. With this function, instead of having to check each collision manually, we can detect any collision between colliders A and B. Keep in mind, the collider struct used in this would have to contain information on the top, bottom, left and right co-ordinates on each collider. You can then store all colliders in an array and then index through them to check for collisions. The function:
int collisionFunction(collider * A, collider * B){
//Check to see if the colliders are "lined up" on the X-axis
if( (A->right > B->left ) && (B->right > A->left) ){
//Check to see if the colliders are also "lined up" on the Y-axis
if( (A->top < B->bottom) && (B->top < A->bottom) ){
return 1; // COLLISION DETECTED
}
}
return 0;//NO COLLISION DETECTED
}
Explanation of the function/algorithm: First of all, we check to see if A and B are "lined up" on the x axis. By "lined up", I mean to say that two colliders could be colliding based off their position on the X-axis. Then, we check to see if each collider is lined up on the Y-axis. If both conditions are met, then the colliders are colliding. It can be a little hard to grasp this at first so I suggest you trace this by drawing out shapes on paper (some colliding, others not) and see whether the algorithm says they're colliding or not. This algorithm will work for coordinate systems where the origin (i.e. (0,0) ) is in the top left corner of the screen, which is the convention for 2D graphics.
Keep in mind that your player would go through the wall partially when using this algorithm - this is very common in 2D games. But, given the number of pixels you're using, this could obviously be a problem. Therefore, you should take that into account when implementing this algorithm.

How about this?
int wall_collision(Sprite *w_sprite)
{
if(w_sprite->left >= wall_sprite->right) return 0;
if(w_sprite->right <= wall_sprite->left) return 0;
if(w_sprite->top >= wall_sprite->bottom) return 0;
if(w_sprite->bottom <= wall_sprite->top) return 0;
return 1;
}
Left/Right/Top/Bottom could be values or functions, or just replace them with the actual values. The "left" and "top" would be the same as the x/y value of the sprite or wall. The "right" and "bottom" would be the x/y + the width/height in pixels of the sprite or wall, respectively.
Take a look at this link for a more in-depth tutorial on simple collision detection: http://lazyfoo.net/SDL_tutorials/lesson17/index.php
EDIT: The example code assumes a coordinate system where x increases as you go right, and y increases as you go down.

Related

Intesection problem with Möller-Trumbore algorithm on 1 dimension of the triangle

I am currently working on a raytracer project and I just found out a issue with the triangle intersections.
Sometimes, and I don't understand when and why, some of the pixels of the triangle don't appear on the screen. Instead I can see the object right behind it. It only occurs on one dimension of the triangle and it depends on the camera and the triangle postions (e.g. picture below).
Triangle with pixels missing
I am using Möller-Trumbore algorithm to compute every intersection. Here's my implementation :
t_solve s;
t_vec v1;
t_vec v2;
t_vec tvec;
t_vec pvec;
v1 = vec_sub(triangle->point2, triangle->point1);
v2 = vec_sub(triangle->point3, triangle->point1);
pvec = vec_cross(dir, v2);
s.delta = vec_dot(v1, pvec);
if (fabs(s.delta) < 0.00001)
return ;
s.c = 1.0 / s.delta;
tvec = vec_sub(ori, triangle->point1);
s.a = vec_dot(tvec, pvec) * s.c;
if (s.a < 0 || s.a > 1)
return ;
tvec = vec_cross(tvec, v1);
s.b = vec_dot(dir, tvec) * s.c;
if (s.b < 0 || s.a + s.b > 1)
return ;
s.t1 = vec_dot(v2, tvec) * s.c;
if (s.t1 < 0)
return ;
if (s.t1 < rt->t)
{
rt->t = s.t1;
rt->last_obj = triangle;
rt->flag = 0;
}
The only clue at the moment is that by using a different method of calculating my ray (called dir in the code), the result is that I have less pixels missing.
Moreover, when I turn the camera and look behind, I see that the bug occurs on the opposite side of the triangle. All of this make me think that the issue is mainly linked with the ray..
Take a look at Watertight Ray/Triangle Intersection. I would much appropriate if you could provide a minimal example where a ray should hit the triangle, but misses it. I had this a long time ago with the Cornel Box - inside the box there were some "black" pixels because on edges none of the triangles has been hit. It's a common problem stemming from floating-point imprecision.

vf_codecview.c printing motion vectors in 'future' only

I am trying to understand the way ffmpeg draws motion vectors.
I went through the vf_codecview.c file and saw the function draw_arrow which takes only those vectors where source > 0 implying only those from the future.
Does anyone know why is this? And what's the use of computing past and future, if ffmpeg through this file takes only the future?
Doesn't appear to be the case
source can take on either positive or negative value.
/**
* Where the current macroblock comes from; negative value when it comes
* from the past, positive value when it comes from the future.
* XXX: set exact relative ref frame reference instead of a +/- 1 "direction".
*/
int32_t source;
In the call you linked to, we have
if ((direction == 0 && (s->mv & MV_P_FOR) && frame->pict_type == AV_PICTURE_TYPE_P) ||
(direction == 0 && (s->mv & MV_B_FOR) && frame->pict_type == AV_PICTURE_TYPE_B) ||
(direction == 1 && (s->mv & MV_B_BACK) && frame->pict_type == AV_PICTURE_TYPE_B))
draw_arrow(frame->data[0], mv->dst_x, mv->dst_y, mv->src_x, mv->src_y,
frame->width, frame->height, frame->linesize[0],
100, 0, mv->source > 0);
If the macroblock is predicted from a past frame, the last expression evaluates to and is passed as 0, else 1.
The conditional clearly allows for both past- and future- predicted macroblocks.
P.S. you are looking at the source for ver 2.5, which is very old, at this point. Current version is at https://ffmpeg.org/doxygen/trunk/vf__codecview_8c_source.html#l00256

Opengl make "AI" paddle move up and down

I'm a bit of an openGL/programming noobie so I'm trying to make an "AI" for the right paddle. I know this isnt the proper way of doing it, what I SHOULD be doing is making it follow the ball. But right now I'm just trying to make it perpetually move up and down. I can't figure out how to do it, trying to use If loops like
if (paddle.pos[1] > 1){
paddle.pos[1] = paddle.pos[1] - delta}
I set delta to something like 0.01, 1 is the top of the screen. Obviously this isnt right because as soon as it goes down below 1 it goes up again, but I'm trying to do something like it.
2nd question - How do you move the ball from 0,0 when it starts? Kind of the same problem, am using if statements with the x values but thats definitely not right.
This is using C by the way.
Try something like this to make pos repeatedly go from 0 to 1 and back to 0:
// Initialize.
float pos = 0.0f;
float delta = 0.01f;
// On every update.
pos += delta;
if (pos > 1.0f) {
pos = 1.0f;
delta = -delta;
} else if (pos < 0.0f) {
pos = 0.0f;
delta = -delta;
}
The key here is that you invert the sign of your increment each time you reach one of the end positions.

Standard deviation of pixel values in a masked image

I have a DICOM image with a mask on. It looks like a black background with a white circle in the middle (area not covered and zeroed with the mask).
The code for which is:
import numpy as np
import dicom
import pylab
ds = dicom.read_file("C:\Users\uccadmin\Desktop\James_Phantom_CT_Dec_16th\James Phantom CT Dec 16th\Images\SEQ4Recon_3_34\IM-0268-0001.dcm")
lx, ly = ds.pixel_array.shape
X, Y = np.ogrid[0:lx, 0:ly]
mask = (X - lx/2)**2 + (Y - ly/2)**2 > lx*ly/8 # defining mask
ds.pixel_array[mask] = 0
print np.std(ds.pixel_array) # trying to get standard deviation
pylab.imshow(ds.pixel_array, cmap=pylab.cm.bone) # shows image with mask
I want to get the standard deviation of the pixel values INSIDE the white circle ONLY i.e. exclude the black space outside the circle (the mask).
I do not think the value I am getting with the above code is correct, as it is ~500, and the white circle is almost homogenous.
Any ideas how to make sure that I get the standard deviation of the pixel values within the white circle ONLY in a Pythonic way?
I think the reason you are getting a big number is because your standard deviation is including all the zero values.
Is it enough for you to simply ignore all zero values? (This will be okay, providing that no or very few pixels in the circle have value 0.) If so
np.std([x for x in ds.pixel_array if x > 0])
should do the trick. If this isn't good enough, then you can reverse the condition in your mask to be
mask = (X - lx/2)**2 + (Y - ly/2)**2 < lx*ly/8 # defining mask, < instead of >
and do
mp.std(ds.pixel_array[mask])

Pruning short line segments from edge detector output?

I am looking for an algorithm to prune short line segments from the output of an edge detector. As can be seen in the image (and link) below, there are several small edges detected that aren't "long" lines. Ideally I'd like just the 4 sides of the quadrangle to show up after processing, but if there are a couple of stray lines, it won't be a big deal... Any suggestions?
Image Link
Before finding the edges pre-process the image with an open or close operation (or both), that is, erode followed by dilate, or dilate followed by erode. this should remove the smaller objects but leave the larger ones roughly the same.
I've looked for online examples, and the best I could find was on page 41 of this PDF.
I doubt that this can be done with a simple local operation. Look at the rectangle you want to keep - there are several gaps, hence performing a local operation to remove short line segments would probably heavily reduce the quality of the desired output.
In consequence I would try to detect the rectangle as important content by closing the gaps, fitting a polygon, or something like that, and then in a second step discard the remaining unimportant content. May be the Hough transform could help.
UPDATE
I just used this sample application using a Kernel Hough Transform with your sample image and got four nice lines fitting your rectangle.
In case somebody steps on this thread, OpenCV 2.x brings an example named squares.cpp that basically nails this task.
I made a slight modification to the application to improve the detection of the quadrangle
Code:
#include "highgui.h"
#include "cv.h"
#include <iostream>
#include <math.h>
#include <string.h>
using namespace cv;
using namespace std;
void help()
{
cout <<
"\nA program using pyramid scaling, Canny, contours, contour simpification and\n"
"memory storage (it's got it all folks) to find\n"
"squares in a list of images pic1-6.png\n"
"Returns sequence of squares detected on the image.\n"
"the sequence is stored in the specified memory storage\n"
"Call:\n"
"./squares\n"
"Using OpenCV version %s\n" << CV_VERSION << "\n" << endl;
}
int thresh = 70, N = 2;
const char* wndname = "Square Detection Demonized";
// helper function:
// finds a cosine of angle between vectors
// from pt0->pt1 and from pt0->pt2
double angle( Point pt1, Point pt2, Point pt0 )
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
void findSquares( const Mat& image, vector<vector<Point> >& squares )
{
squares.clear();
Mat pyr, timg, gray0(image.size(), CV_8U), gray;
// karlphillip: dilate the image so this technique can detect the white square,
Mat out(image);
dilate(out, out, Mat(), Point(-1,-1));
// then blur it so that the ocean/sea become one big segment to avoid detecting them as 2 big squares.
medianBlur(out, out, 3);
// down-scale and upscale the image to filter out the noise
pyrDown(out, pyr, Size(out.cols/2, out.rows/2));
pyrUp(pyr, timg, out.size());
vector<vector<Point> > contours;
// find squares only in the first color plane
for( int c = 0; c < 1; c++ ) // was: c < 3
{
int ch[] = {c, 0};
mixChannels(&timg, 1, &gray0, 1, ch, 1);
// try several threshold levels
for( int l = 0; l < N; l++ )
{
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
if( l == 0 )
{
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
Canny(gray0, gray, 0, thresh, 5);
// dilate canny output to remove potential
// holes between edge segments
dilate(gray, gray, Mat(), Point(-1,-1));
}
else
{
// apply threshold if l!=0:
// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
gray = gray0 >= (l+1)*255/N;
}
// find contours and store them all as a list
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
vector<Point> approx;
// test each contour
for( size_t i = 0; i < contours.size(); i++ )
{
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
// square contours should have 4 vertices after approximation
// relatively large area (to filter out noisy contours)
// and be convex.
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if( approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx)) )
{
double maxCosine = 0;
for( int j = 2; j < 5; j++ )
{
// find the maximum cosine of the angle between joint edges
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
// if cosines of all angles are small
// (all angles are ~90 degree) then write quandrange
// vertices to resultant sequence
if( maxCosine < 0.3 )
squares.push_back(approx);
}
}
}
}
}
// the function draws all the squares in the image
void drawSquares( Mat& image, const vector<vector<Point> >& squares )
{
for( size_t i = 1; i < squares.size(); i++ )
{
const Point* p = &squares[i][0];
int n = (int)squares[i].size();
polylines(image, &p, &n, 1, true, Scalar(0,255,0), 3, CV_AA);
}
imshow(wndname, image);
}
int main(int argc, char** argv)
{
if (argc < 2)
{
cout << "Usage: ./program <file>" << endl;
return -1;
}
static const char* names[] = { argv[1], 0 };
help();
namedWindow( wndname, 1 );
vector<vector<Point> > squares;
for( int i = 0; names[i] != 0; i++ )
{
Mat image = imread(names[i], 1);
if( image.empty() )
{
cout << "Couldn't load " << names[i] << endl;
continue;
}
findSquares(image, squares);
drawSquares(image, squares);
imwrite("out.jpg", image);
int c = waitKey();
if( (char)c == 27 )
break;
}
return 0;
}
The Hough Transform can be a very expensive operation.
An alternative that may work well in your case is the following:
run 2 mathematical morphology operations called an image close (http://homepages.inf.ed.ac.uk/rbf/HIPR2/close.htm) with a horizontal and vertical line (of a given length determined from testing) structuring element respectively. The point of this is to close all gaps in the large rectangle.
run connected component analysis. If you have done the morphology effectively, the large rectangle will come out as one connected component. It then only remains iterating through all the connected components and picking out the most likely candidate that should be the large rectangle.
Perhaps finding the connected components, then removing components with less than X pixels (empirically determined), followed by dilation along horizontal/vertical lines to reconnect the gaps within the rectangle
It's possible to follow two main techniques:
Vector based operation: map your pixel islands into clusters (blob, voronoi zones, whatever). Then apply some heuristics to rectify the segments, like Teh-Chin chain approximation algorithm, and make your pruning upon vectorial elements (start, endpoint, length, orientation and so on).
Set based operation: cluster your data (as above). For every cluster, compute principal components and detect lines from circles or any other shape by looking for clusters showing only 1 significative eigenvalue (or 2 if you look for "fat" segments, that could resemble to ellipses). Check eigenvectors associated with eigenvalues to have information about orientation of the blobs, and make your choice.
Both ways could be easily explored with OpenCV (the former, indeed, falls under "Contour analysis" category of algos).
Here is a simple morphological filtering solution following the lines of #Tom10:
Solution in matlab:
se1 = strel('line',5,180); % linear horizontal structuring element
se2 = strel('line',5,90); % linear vertical structuring element
I = rgb2gray(imread('test.jpg'))>80; % threshold (since i had a grayscale version of the image)
Idil = imdilate(imdilate(I,se1),se2); % dilate contours so that they connect
Idil_area = bwareaopen(Idil,1200); % area filter them to remove the small components
The idea is to basically connect the horizontal contours to make a large component and filter by an area opening filter later on to obtain the rectangle.
Results:

Resources