Removing an image border using opencv - image

I have a lot of images with a thick white border around them. What's the easiest way for me to remove/isolate these borders? Anyway I can do this using OpenCV?

Not a very elegant solution, but here's my first attempt.
if( direction == BORDER_SEARCH_DIRECTION.TOP ||
direction == BORDER_SEARCH_DIRECTION.BOTTOM )
{
for (int y = startY; y >= 0 && y < _image.Height; y += deltaY)
{
bool foundNonWhite = false;
for (int x = startX; x < _image.Width; x += deltaX)
{
if (Convert.ToInt32(_image.ManagedArray.GetValue(y, x, 0)) < BORDER_WHITENESS_THRESHOLD)
{
foundNonWhite = true;
break;
}
}
if (!foundNonWhite)
{
borderAt = y;
break;
}
}
}
Basically, this assumes that a border will start at most a fifth of the way into the image from the top/bottom, and it searches for the first row that is completely "white". I run the routine twice and if I find an entire row of white on the top and bottom of the image, I assume we have a border.
Damn thing takes 250 milliseconds.

One approach would be to use quad-tree decomposition such that the border area is a "leaf" node. Then you can subtract it out.

Related

Constrain mouse daw to 45degrees Processing

How does one constrain interactively drawing a line to 45degrees?
Imagine there in an underlining grid that's at 45 degees which the mouse draw is magnetized too. Perhaps on mousedown decides which your starting position is and after that your Mouse.X and Mouse.Y position only update in 45 degrees from that starting click?
float dif = 10;
float easing = 0.05;
boolean current = false;
boolean started = false;
float rainbow, x, y;
float colbase;
PVector pos, prev_pos, update_pos;
float step = 25;
void setup(){
size(400, 400);
background(100);
colorMode(HSB);
}
void draw(){
if (rainbow >= 255) rainbow=0; else rainbow+=5;
if (frameCount % dif == 0) {
colbase = rainbow;
}
if(mousePressed){
update_pos();
if(current){
started = true;//start drawing
pos = update_pos;
}else{
prev_pos = update_pos;
}
current = !current;
}else{
update_pos();
started = false;
pos = update_pos;
prev_pos = update_pos;
}
if(started){
//style for lines
strokeWeight(step);
stroke(colbase,255,255);
noFill();
line(prev_pos.x, prev_pos.y, pos.x, pos.y);
}
}
PVector update_pos(){
x = lerp(x, mouseX, easing);
y = lerp(y, mouseY, easing);
update_pos = new PVector(x, y);
return update_pos;
}
I would like to say that I like this. Also, that this question was ultimately way harder to answer than I though it would be.
Here's the result:
First, I took the liberty to reorganize your example code. I don't know how experienced you are, and maybe some of the things I changed were on purpose, but it seemed to me that your example had a lot of weird code bits dangling. Here's the example code with my changes (check the next code block for the answer, this one is more like a code review to help you in general):
float dif = 10;
float easing = 0.05;
boolean current, started; // automatically initializing as false unless stated otherwise
float rainbow;
float colbase;
PVector pos, prev_pos;
float step = 25;
void setup() {
size(400, 400);
background(100);
colorMode(HSB); // clever!
pos = prev_pos = new PVector(); // instanciating some non-null PVectors
}
void draw() {
pos = prev_pos = update_pos(); // cascade attribution: it starts by the last one and goes back toward 'pos'
if (mousePressed) {
if (!started) {
// initializing variables needed for drawing
started = true;
pos = prev_pos = new PVector(mouseX, mouseY);
}
} else {
started = false;
}
if (started) {
updateColor();
strokeWeight(step);
stroke(colbase, 255, 255);
noFill();
line(prev_pos.x, prev_pos.y, pos.x, pos.y);
}
}
void updateColor() {
if (rainbow >= 255) {
rainbow=0;
} else {
rainbow+=5;
}
if (frameCount % dif == 0) {
colbase = rainbow;
}
}
PVector update_pos() {
float x = lerp(pos.x, mouseX, easing);
float y = lerp(pos.y, mouseY, easing);
return new PVector(x, y);
}
Notice how I changed a couple variables names. As a general rule, you should always name your variables as if the guy maintaining your code was an angry biker who knows where you live.
Now to solve your actual question:
boolean isDrawing;
float rainbow, colbase, dif, easing, step, centerZone;
PVector pos, prev_pos, origin, quadrant;
void setup() {
size(400, 400);
background(100);
colorMode(HSB); // clever!
centerZone = 5; // determine how close to the original click you must be to change quadrant
dif = 10;
easing = 0.05;
step = 25;
origin = pos = prev_pos = new PVector();
}
void draw() {
updatePosition();
if (mousePressed) {
if (!isDrawing) {
// setting variables needed for drawing
isDrawing = true;
origin = pos = prev_pos = new PVector(mouseX, mouseY); // drawing should always start where the mouse currently is
}
} else {
isDrawing = false;
}
if (isDrawing) {
updateColor();
strokeWeight(step);
stroke(colbase, 255, 255);
noFill();
line(prev_pos.x, prev_pos.y, pos.x, pos.y);
}
}
void updateColor() {
if (rainbow >= 255) {
rainbow=0;
} else {
rainbow+=5;
}
if (frameCount % dif == 0) {
colbase = rainbow;
}
}
void updatePosition() {
float relativeX = pos.x - origin.x;
float relativeY = pos.y - origin.y;
float diffX = mouseX - origin.x;
float diffY = mouseY - origin.y;
float distance = abs(diffX) > abs(diffY) ? abs(diffX) : abs(diffY); // this is just inline if, the syntax being " condition ? return if true : return if false; "
prev_pos = pos;
// validating if the mouse is in the same quadrant as the pencil
PVector mouseQuadrant = new PVector(diffX > 0 ? 1 : -1, diffY > 0 ? 1 : -1);
// we can only change quadrant when near the center
float distanceFromTheCenter = abs(relativeX) + abs(relativeY);
if (quadrant == null || distanceFromTheCenter < centerZone) {
quadrant = new PVector(diffX > 0 ? 1 : -1, diffY > 0 ? 1 : -1);
}
// if the mouse left it's quadrant, then draw toward the center until close enough to change direction
// ^ is the XOR operator, which returns true only when one of the sides is different than the other (one true, one false)
// if the quadrant info is positive and the diff coordinate is negative (or the other way around) we know the mouse has changed quadrant
if (distanceFromTheCenter > centerZone && (relativeX > 0 ^ mouseQuadrant.x > 0 || relativeY > 0 ^ mouseQuadrant.y > 0)) {
// going toward origin
pos = new PVector(lerp(prev_pos.x, origin.x, easing), lerp(prev_pos.y, origin.y, easing));
} else {
// drawing normally
pos = new PVector(lerp(prev_pos.x, origin.x + (distance * quadrant.x), easing), lerp(prev_pos.y, origin.y + (distance * quadrant.y), easing));
}
}
I'll answer your questions on this code if you have any. Have fun!
EDIT:
This part could use more explainations, so let's elaborate a little bit:
if (distanceFromTheCenter > centerZone && (relativeX > 0 ^ mouseQuadrant.x > 0 || relativeY > 0 ^ mouseQuadrant.y > 0)) {
// going toward origin
} else {
// drawing normally
}
The point of this check is to know if the mouse is still in the same quadrant than the "pencil", by which I mean the point where we're drawing.
But what are the "quadrants"? Remember, the user can only draw in 45 degrees lines. Theses lines are defines in relation to the point where the user clicked to draw, which I call "origin":
Which means that the screen is always divided into 4 different "quadrants". Why? Because there is 4 different directions where we can draw. But we don't want the user to have to stick to these exact pixels to be able to draw, do we? We could, but that's not how the algo is working: it poses a pencil on the page and then follows the mouse. So here if the mouse goes up-left from origin, the pencil will draw on the up-left branch of the 45 degrees X lines. Each of these imaginary lines has it's own "part of the screen" where they control where the pencil has the right to draw, which I call "quadrants":
Now with this algorithm we can force the pencil over the 45 degrees lines, but what happens if the mouse goes from one quadrant to another one? I figured out that the pencil should follow, but without breaking the "only drawing in 45 degrees lines" rule, so it has to go through the center before changing quadrant:
There is no big secret here. It's kind of easy to figure the business rules we want to apply:
While the mouse and the pencil are in the same quadrant, follow the mouse's position to draw (but only on the line).
If the mouse is in a different quadrant from the pencil, draw toward the center, then toward the mouse normally.
Which is exactly what we're doing here:
if (mouse and pencil are in different quadrants) {
// draw toward origin
} else {
// draw toward the mouse
}
Then... if it's so simple, why this convoluted code?
(distanceFromTheCenter > centerZone && (relativeX > 0 ^ mouseQuadrant.x > 0 || relativeY > 0 ^ mouseQuadrant.y > 0))
Well, really, it could be written in a waaay easier to read way, but it would be longer. Let's decompose it and see why it's written this way:
My operational logic here go as follow:
As you probably know, point (0,0) is on the upper-left side of the sketch. Most drawing in computer science calculate coordinates this way.
Quadrants exist in relation to the origin point (where the user clicked to start drawing).
Relative coordinates in each quadrant will be either positive or negative. X will be positive if they are to the right of origin. Y will be positive if they are lower in the screen than origin:
If you follow my logic, when relative coordinate of the mouse and pencil aren't both positive or both negative in the same way, it would mean that they are not located in the same quadrant.
So:
distanceFromTheCenter > centerZone => when we're close enough to the center, we can let the pencil switch direction. No need to calculate the rest if the pencil isn't near the center.
relativeX > 0 ^ mouseQuadrant.x > 0 => relativeX is really just pos.x - origin.x. It's where the pencil is in relation to origin. mouseQuadrant "knows" where the mouse is in relation to the origin (it's just diffX and diffY as interpreted for later use, in fact we totally could have used diffX and diffY instead as we just compare if they are positive or negative numbers)
Why the XOR operator if it's so simple? Because of this:
relativeX > 0 ^ mouseQuadrant.x > 0
// is the same thing than this pseudocode:
if (relativeX sign is different than mousequadrant.x's sign)
// and the same thing than this more elaborated code:
!(relativeX > 0 && mouseQuadrant.x > 0) || !(relativeX < 0 && mouseQuadrant.x < 0)
// or, in a better writing:
(relativeX > 0 && mouseQuadrant.x < 0) || (relativeX < 0 && mouseQuadrant.x > 0)
...which is also convoluted and also ugly. So, really, (relativeX > 0 ^ mouseQuadrant.x > 0 || relativeY > 0 ^ mouseQuadrant.y > 0) is just a short hand for:
(!(relativeX > 0 && mouseQuadrant.x > 0) || !(relativeX < 0 && mouseQuadrant.x < 0) || !(relativeY > 0 && mouseQuadrant.y > 0) || !(relativeY < 0 && mouseQuadrant.y < 0))
I hope this just made sense! Have fun!

Processing: collisions with trails

Dealing with collisions in Processing is straightforward. However, how do I identify collisions with trails?
Example: imagine the light cycles in Tron, if you wish, with the alteration that trails of derezzed light cycles do not disappear. In Tron, if a cycle intersects any point another cycle, itself included, has ever been, it 'dies'. How do I efficiently find this event in Processing?
One hacky workaround is to draw the line into a PImage and check if the colour at a location is the same as the background or not (e.g. a pre-existing line, hence a collision).
Here's a rough (and slightly inefficient (due to get()/set() calls) proof of concept:
PImage buffer;
//how mutch we scale down = how pixely this will look
int multiplier = 8;
//scaled down width/height
int w,h;
//cursor position
int px,py;
//cursor velocity;
int vx,vy;
void setup(){
size(400,400);
noSmooth();
w = width / multiplier;
h = height / multiplier;
buffer = createImage(w,h,RGB);
clear();
}
void clear(){
java.util.Arrays.fill(buffer.pixels,color(0));
buffer.updatePixels();
}
void draw(){
//update cursor
px += vx;
py += vy;
//check edges
if(px < 0){
px = w-1;
}
if(px > w){
px = 0;
}
if(py < 0){
py = h-1;
}
if(py > h){
py = 0;
}
//check collision
if(keyPressed){
if(keyCode == UP || keyCode == DOWN || keyCode == LEFT || keyCode == RIGHT){
checkSelfIntersection();
}
}
//paint cursor
buffer.set(px,py,color(0,192,0));
//render on screen
image(buffer,0,0,width,height);
}
void checkSelfIntersection(){
//if the pixel ahead is not the same colour as the background
if(buffer.get(px+vx,py+vy) > color(0)){
clear();
println("Cycle go BOOM!");
}
}
void keyPressed(){
if(keyCode == UP){
vy = -1;
}
if(keyCode == DOWN){
vy = +1;
}
if(keyCode == LEFT){
vx = -1;
}
if(keyCode == RIGHT){
vx = +1;
}
}
void keyReleased(){
vx = vy = 0;
}
A similar concept can be done be keeping track of points in a list and checking if a new point is already part of this list (collision) or not:
ArrayList<PVector> path = new ArrayList<PVector>();
//cursor position
int px,py;
//cursor velocity;
int vx,vy;
void setup(){
size(400,400);
noFill();
strokeWeight(10);
}
void draw(){
//update cursor
px += vx;
py += vy;
//check edges
if(px < 0){
px = 0;
}
if(px > width){
px = width;
}
if(py < 0){
py = 0;
}
if(py > height){
py = height;
}
//check collision
if(keyPressed){
if(keyCode == UP || keyCode == DOWN || keyCode == LEFT || keyCode == RIGHT){
checkSelfIntersection();
}
}
background(255);
beginShape();
for(int i = 0 ; i < path.size(); i++){
PVector p = path.get(i);
vertex(p.x,p.y);
}
endShape();
}
void checkSelfIntersection(){
PVector cursor = new PVector(px,py);
if(path.contains(cursor)){
path.clear();
println("Cycle go BOOM!");
}else{
path.add(cursor);
}
}
void keyPressed(){
if(keyCode == UP){
vy = -5;
}
if(keyCode == DOWN){
vy = +5;
}
if(keyCode == LEFT){
vx = -5;
}
if(keyCode == RIGHT){
vx = +5;
}
}
void keyReleased(){
vx = vy = 0;
}
The concept isn't that different from how games like Snake/Volfied/etc. check for self intersections.
Note I'm cheating a bit by updating a "cursor" on keys with a small velocity: this avoid gaps in the lines. If you try to replace with the mouse, you'll notice the collision check may fail if the mouse moves fast as it checks one point against a list of recorded points. An alternative might be to split the list of points into pairs of lines and check if the new point intersects any of them.
You might want to also check this similar question
Stack Overflow isn't really designed for general "how do I do this" type questions. It's for specific "I tried X, expected Y, but got Z instead" type questions. That being said, I'll try to help in a general sense.
You can probably just keep track of the lines formed by the cycles, in the form of an ArrayList of all of the points where a player turned. Then at each step, you can check whether the player is intersecting with any of those lines.
More specifically, you'd probably want to form another line between the previous player coordinate and the next player coordinate. Then check whether that line intersects with any of the other lines using formulas that I'm sure you can find through a google search or two.
You probably don't have to do anything smarter than that unless you're talking about very very large playing fields (as in, millions of lines). So it's a little early to ask about efficiency.
There are of course many other ways to approach the problem. You might also use a 2D array that keeps track of the trails, or you could use pixel-based collision, or probably any number of other solutions. The point is you need to try something and post a MCVE along with a specific question if you get stuck, and we'll go from there. Good luck.

Creating a view with shadow in a Processing game

I am writing a game for school project using Processing. I am currently dealing with a player's field of view. The player's field of view is basically a circle, but I would like the view to be blocked if there is an obstacle in front, meaning that you can't see the things behind the obstacle. The below image is the current results I have.
The link to the image
My code: http://pastie.org/10854654
The method I used is to go through every pixel in the player's field of view starting from the center, picking a path towards the circumference. As I searched outwards, if an obstacle is found on the path, I then draw a black line on the rest of the path. Changing the direction of the path degree by degree, eventually covering the whole circle.
//Draw a circle field of view.
int[][] collisionMap = map.getCollisionMap();
//Use a lot of small rectangle to cover the full map except of the circle field of view.
mainapplet.fill(0, 0, 0, 128);
for(int i = 0; i <= MyApplet.width; i++ ){
for(int j = 0; j <= MyApplet.height; j++ ){
if(mainapplet.dist(playerx, playery, i, j) > FieldOfView)
mainapplet.rect(i, j, 1, 1);
}
}
//Scan the circle field of view. If there is collision , draw a line to cover the area ,which means that the area is invisible.
mainapplet.stroke(0, 0, 0, 128);
mainapplet.strokeWeight(5);
for(float i = 0; i < 360; i+=1) {
for(float j = 0; j < FieldOfView ; j++ ){
float x = j * mainapplet.cos( mainapplet.radians(i) );
float y = j * mainapplet.sin( mainapplet.radians(i) );
if(collisionMap[player.getX() + (int)x ][player.getY() + (int)y ] == 1){
mainapplet.line(playerx + x, playery + y,
playerx + (FieldOfView-1)* mainapplet.cos( mainapplet.radians(i) ),
playery + (FieldOfView-1)* mainapplet.sin( mainapplet.radians(i) )
);
break;
}
}
}
collisionMap is a 2D array with 0s and 1s, "1" denoting that an obstacle is present at the location.
However, I find this method inefficient, therefore, causing lag. Is there a better way to do this? Or maybe there are already written tools that I can use?
What you're talking about is called 2d shadow mapping. It's not a trivial topic, but there are a ton of resources on google. I highly recommend reading up on them, because they'll explain it much better than I can.
But I did find this sketch on OpenProcessing which does what you describe. The full code is available at that link, but the pertinent bit seems to be this function:
void drawShadow() {
PVector tmp;
PVector m = new PVector(mouseX, mouseY); //mouse vector
fill(0);
stroke(0);
for (int i=0; i < vertCnt; i++) {
beginShape();
PVector v1 = p[i]; //current vertex
PVector v2 = p[i==vertCnt-1?0:i+1]; //"next" vertex
vertex(v2.x, v2.y);
vertex(v1.x, v1.y);
//current shadow vertex
tmp = PVector.sub(v1, m);
tmp.normalize();
tmp.mult(5000); //extend well off screen
tmp.add(v1); //true up position
vertex(tmp.x, tmp.y);
//"next" shadow vertex
tmp = PVector.sub(v2, m);
tmp.normalize();
tmp.mult(5000); //extend well off screen
tmp.add(v2); //true up position
vertex(tmp.x, tmp.y);
endShape(CLOSE);
}
}
But honestly the best thing you can do is google "processing shadow mapping" and spend some time reading through the results. This is a huge topic that's a bit too broad for a single Stack Overflow question.

Least distance from a point to an area

I am trying to find a point (P2) in a closed area that has the minimum distance to a point (P1). The area is built of homogenous pixels, it is not shaped perfectly and it is not necessarily convex. This is basically a problem of reaching an area from the shortest path.
The whole space is a stored in the form of a bitmap in the memory. What is the best method to find P2? Should I go with random search (optimization) methods? Optimization methods do not give the exact minimum but they are faster than brute forcing every pixel of the area. I need to perform thousands of these decisions in a few seconds.
The MinX,MinY,MaxX,MaxY of the area is available.
Thanks.
Here is my code, it's a discrete version using discrete coordinates:
Hint: the method I used to find the circumference of the Area is simple, it's like how do you know the beach from the land ? answer: the beach is covered by Sea from one side, so in my graph matrix, NULL reference is Sea, Points are Land!
Class Point:
class Point
{
public int x;
public int y;
public Point (int X, int Y)
{
this.x = X;
this.y = Y;
}
}
Class Area:
class Area
{
public ArrayList<Point> points;
public Area ()
{
p = new ArrayList<Point>();
}
}
Discrete Distance Utility Class:
class DiscreteDistance
{
public static int distance (Point a, Point b)
{
return Math.sqrt(Math.pow(b.x - a.x,2), Math.pow(b.y - a.y,2))
}
public static int distance (Point a, Area area)
{
ArrayList<Point> cir = circumference(area);
int d = null;
for (Point b : cir)
{
if (d == null || distance(a,b) < d)
{
d = distance(a,b);
}
}
return d;
}
ArrayList<Point> circumference (Area area)
{
int minX = 0;
int minY = 0;
int maxX = 0;
int maxY = 0;
for (Point p : area.points)
{
if (p.x < minX) minX = p.x;
if (p.x > maxX) maxX = p.x;
if (p.y < minY) minY = p.y;
if (p.y > maxY) maxY = p.y;
}
int w = maxX - minX +1;
int h = maxY - minY +1;
Point[][] graph = new Point[w][h];
for (Point p : area.points)
{
graph[p.x - minX][p.y - minY] = p;
}
ArrayList<Point> cir = new ArrayList<Point>();
for (int i=0; i<w; i++)
{
for (int j=0; j<h; j++)
{
if ((i > 0 && graph[i-1][j] == null)
|| (i < (w-1) && graph[i+1][j] == null)
|| (j > 0 && graph[i][j-1] == null)
|| (i < (h-1) && graph[i][j+1] == null))
{
cir.add(graph[i][j]);
}
}
}
return cir;
}
}
We have to assume you know or can easily find at least one pixel address (x0,y0) inside the area. The fastest solution will certainly be to search from this pixel in a straight line, say in the plus x direction Alternately, since you have a bounding box, pick the compass point toward the nearest boundary and go in that direction.
When you find the edge of the region, search depth first along the boundary. For general polygons with self-intersections and/or holes, this will have to be a complete and carefully implemented DFS maintaining a set of already-visited vertices. Only if the polygon is simple will it suffice to remember only the last-visited pixel to avoid doubling back over what's already searched.
During the DFS, compute the distance squared to p1 for each boundary pixel and track the minimum.
Note, if you are really pressed for performance this distance squared can be updated incrementally to replace multiplications with additions. I.e. if you know d2=(x2-x1)^2+(y2-y1)^2 and then increment x2 by 1 to take the next step around the boundary, the new squared distance is
((x2+1) - x1)^2 + (y2-y1)^2 = d2 + 2(x2 - x1) + 1
So you can update d2 with d2 += 2(x2 - x1) + 1. The multiplication by 2 is of course just a left shift, so this is very cheap. There are similar very cheap updates for steps in each direction.
One approach might be to set for an approximate solution by first canculating a triangulation of the area; afterwards, only the corners of the triangles have to be checked. This approach could be beneficial especially if in the many evaluations you plan for, the outside point changes but the shape itself does not change.
You could find the center of the rect of the area, and use a triangle between the two points to find the angle, and then use a function f(x) = mx + b to do the pixel walk until you find a pixel of the area to calculate the distance, and then rotate the angle until you find the shortest path.

Finding closest non-black pixel in an image fast

I have a 2D image randomly and sparsely scattered with pixels.
given a point on the image, I need to find the distance to the closest pixel that is not in the background color (black).
What is the fastest way to do this?
The only method I could come up with is building a kd-tree for the pixels. but I would really want to avoid such expensive preprocessing. also, it seems that a kd-tree gives me more than I need. I only need the distance to something and I don't care about what this something is.
Personally, I'd ignore MusiGenesis' suggestion of a lookup table.
Calculating the distance between pixels is not expensive, particularly as for this initial test you don't need the actual distance so there's no need to take the square root. You can work with distance^2, i.e:
r^2 = dx^2 + dy^2
Also, if you're going outwards one pixel at a time remember that:
(n + 1)^2 = n^2 + 2n + 1
or if nx is the current value and ox is the previous value:
nx^2 = ox^2 + 2ox + 1
= ox^2 + 2(nx - 1) + 1
= ox^2 + 2nx - 1
=> nx^2 += 2nx - 1
It's easy to see how this works:
1^2 = 0 + 2*1 - 1 = 1
2^2 = 1 + 2*2 - 1 = 4
3^2 = 4 + 2*3 - 1 = 9
4^2 = 9 + 2*4 - 1 = 16
5^2 = 16 + 2*5 - 1 = 25
etc...
So, in each iteration you therefore need only retain some intermediate variables thus:
int dx2 = 0, dy2, r2;
for (dx = 1; dx < w; ++dx) { // ignoring bounds checks
dx2 += (dx << 1) - 1;
dy2 = 0;
for (dy = 1; dy < h; ++dy) {
dy2 += (dy << 1) - 1;
r2 = dx2 + dy2;
// do tests here
}
}
Tada! r^2 calculation with only bit shifts, adds and subtracts :)
Of course, on any decent modern CPU calculating r^2 = dx*dx + dy*dy might be just as fast as this...
As Pyro says, search the perimeter of a square that you keep moving out one pixel at a time from your original point (i.e. increasing the width and height by two pixels at a time). When you hit a non-black pixel, you calculate the distance (this is your first expensive calculation) and then continue searching outwards until the width of your box is twice the distance to the first found point (any points beyond this cannot possibly be closer than your original found pixel). Save any non-black points you find during this part, and then calculate each of their distances to see if any of them are closer than your original point.
In an ideal find, you only have to make one expensive distance calculation.
Update: Because you're calculating pixel-to-pixel distances here (instead of arbitrary precision floating point locations), you can speed up this algorithm substantially by using a pre-calculated lookup table (just a height-by-width array) to give you distance as a function of x and y. A 100x100 array costs you essentially 40K of memory and covers a 200x200 square around the original point, and spares you the cost of doing an expensive distance calculation (whether Pythagorean or matrix algebra) for every colored pixel you find. This array could even be pre-calculated and embedded in your app as a resource, to spare you the initial calculation time (this is probably serious overkill).
Update 2: Also, there are ways to optimize searching the square perimeter. Your search should start at the four points that intersect the axes and move one pixel at a time towards the corners (you have 8 moving search points, which could easily make this more trouble than it's worth, depending on your application's requirements). As soon as you locate a colored pixel, there is no need to continue towards the corners, as the remaining points are all further from the origin.
After the first found pixel, you can further restrict the additional search area required to the minimum by using the lookup table to ensure that each searched point is closer than the found point (again starting at the axes, and stopping when the distance limit is reached). This second optimization would probably be much too expensive to employ if you had to calculate each distance on the fly.
If the nearest pixel is within the 200x200 box (or whatever size works for your data), you will only search within a circle bounded by the pixel, doing only lookups and <>comparisons.
You didn't specify how you want to measure distance. I'll assume L1 (rectilinear) because it's easier; possibly these ideas could be modified for L2 (Euclidean).
If you're only doing this for relatively few pixels, then just search outward from the source pixel in a spiral until you hit a nonblack one.
If you're doing this for many/all of them, how about this: Build a 2-D array the size of the image, where each cell stores the distance to the nearest nonblack pixel (and if necessary, the coordinates of that pixel). Do four line sweeps: left to right, right to left, bottom to top, and top to bottom. Consider the left to right sweep; as you sweep, keep a 1-D column containing the last nonblack pixel seen in each row, and mark each cell in the 2-D array with the distance to and/or coordinates of that pixel. O(n^2).
Alternatively, a k-d tree is overkill; you could use a quadtree. Only a little more difficult to code than my line sweep, a little more memory (but less than twice as much), and possibly faster.
Search "Nearest neighbor search", first two links in Google should help you.
If you are only doing this for 1 pixel per image, I think your best bet is just a linear search, 1 pixel width box at time outwards. You can't take the first point you find, if your search box is square. You have to be careful
Yes, the Nearest neighbor search is good, but does not guarantee you'll find the 'nearest'. Moving one pixel out each time will produce a square search - the diagonals will be farther away than the horizontal / vertical. If this is important, you'll want to verify - continue expanding until the absolute horizontal has a distance greater than the 'found' pixel, and then calculate distances on all non-black pixels that were located.
Ok, this sounds interesting.
I made a c++ version of a soulution, I don't know if this helps you. I think it works fast enough as it's almost instant on a 800*600 matrix. If you have any questions just ask.
Sorry for any mistakes I've made, it's a 10min code...
This is a iterative version (I was planing on making a recursive one too, but I've changed my mind).
The algorithm could be improved by not adding any point to the points array that is to a larger distance from the starting point then the min_dist, but this involves calculating for each pixel (despite it's color) the distance from the starting point.
Hope that helps
//(c++ version)
#include<iostream>
#include<cmath>
#include<ctime>
using namespace std;
//ITERATIVE VERSION
//picture witdh&height
#define width 800
#define height 600
//indexex
int i,j;
//initial point coordinates
int x,y;
//variables to work with the array
int p,u;
//minimum dist
double min_dist=2000000000;
//array for memorising the points added
struct point{
int x;
int y;
} points[width*height];
double dist;
bool viz[width][height];
// direction vectors, used for adding adjacent points in the "points" array.
int dx[8]={1,1,0,-1,-1,-1,0,1};
int dy[8]={0,1,1,1,0,-1,-1,-1};
int k,nX,nY;
//we will generate an image with white&black pixels (0&1)
bool image[width-1][height-1];
int main(){
srand(time(0));
//generate the random pic
for(i=1;i<=width-1;i++)
for(j=1;j<=height-1;j++)
if(rand()%10001<=9999) //9999/10000 chances of generating a black pixel
image[i][j]=0;
else image[i][j]=1;
//random coordinates for starting x&y
x=rand()%width;
y=rand()%height;
p=1;u=1;
points[1].x=x;
points[1].y=y;
while(p<=u){
for(k=0;k<=7;k++){
nX=points[p].x+dx[k];
nY=points[p].y+dy[k];
//nX&nY are the coordinates for the next point
//if we haven't added the point yet
//also check if the point is valid
if(nX>0&&nY>0&&nX<width&&nY<height)
if(viz[nX][nY] == 0 ){
//mark it as added
viz[nX][nY]=1;
//add it in the array
u++;
points[u].x=nX;
points[u].y=nY;
//if it's not black
if(image[nX][nY]!=0){
//calculate the distance
dist=(x-nX)*(x-nX) + (y-nY)*(y-nY);
dist=sqrt(dist);
//if the dist is shorter than the minimum, we save it
if(dist<min_dist)
min_dist=dist;
//you could save the coordinates of the point that has
//the minimum distance too, like sX=nX;, sY=nY;
}
}
}
p++;
}
cout<<"Minimum dist:"<<min_dist<<"\n";
return 0;
}
I'm sure this could be done better but here's some code that searches the perimeter of a square around the centre pixel, examining the centre first and moving toward the corners. If a pixel isn't found the perimeter (radius) is expanded until either the radius limit is reached or a pixel is found. The first implementation was a loop doing a simple spiral around the centre point but as noted that doesn't find the absolute closest pixel. SomeBigObjCStruct's creation inside the loop was very slow - removing it from the loop made it good enough and the spiral approach is what got used. But here's this implementation anyway - beware, little to no testing done.
It is all done with integer addition and subtraction.
- (SomeBigObjCStruct *)nearestWalkablePoint:(SomeBigObjCStruct)point {
typedef struct _testPoint { // using the IYMapPoint object here is very slow
int x;
int y;
} testPoint;
// see if the point supplied is walkable
testPoint centre;
centre.x = point.x;
centre.y = point.y;
NSMutableData *map = [self getWalkingMapDataForLevelId:point.levelId];
// check point for walkable (case radius = 0)
if(testThePoint(centre.x, centre.y, map) != 0) // bullseye
return point;
// radius is the distance from the location of point. A square is checked on each iteration, radius units from point.
// The point with y=0 or x=0 distance is checked first, i.e. the centre of the side of the square. A cursor variable
// is used to move along the side of the square looking for a walkable point. This proceeds until a walkable point
// is found or the side is exhausted. Sides are checked until radius is exhausted at which point the search fails.
int radius = 1;
BOOL leftWithinMap = YES, rightWithinMap = YES, upWithinMap = YES, downWithinMap = YES;
testPoint leftCentre, upCentre, rightCentre, downCentre;
testPoint leftUp, leftDown, rightUp, rightDown;
testPoint upLeft, upRight, downLeft, downRight;
leftCentre = rightCentre = upCentre = downCentre = centre;
int foundX = -1;
int foundY = -1;
while(radius < 1000) {
// radius increases. move centres outward
if(leftWithinMap == YES) {
leftCentre.x -= 1; // move left
if(leftCentre.x < 0) {
leftWithinMap = NO;
}
}
if(rightWithinMap == YES) {
rightCentre.x += 1; // move right
if(!(rightCentre.x < kIYMapWidth)) {
rightWithinMap = NO;
}
}
if(upWithinMap == YES) {
upCentre.y -= 1; // move up
if(upCentre.y < 0) {
upWithinMap = NO;
}
}
if(downWithinMap == YES) {
downCentre.y += 1; // move down
if(!(downCentre.y < kIYMapHeight)) {
downWithinMap = NO;
}
}
// set up cursor values for checking along the sides of the square
leftUp = leftDown = leftCentre;
leftUp.y -= 1;
leftDown.y += 1;
rightUp = rightDown = rightCentre;
rightUp.y -= 1;
rightDown.y += 1;
upRight = upLeft = upCentre;
upRight.x += 1;
upLeft.x -= 1;
downRight = downLeft = downCentre;
downRight.x += 1;
downLeft.x -= 1;
// check centres
if(testThePoint(leftCentre.x, leftCentre.y, map) != 0) {
foundX = leftCentre.x;
foundY = leftCentre.y;
break;
}
if(testThePoint(rightCentre.x, rightCentre.y, map) != 0) {
foundX = rightCentre.x;
foundY = rightCentre.y;
break;
}
if(testThePoint(upCentre.x, upCentre.y, map) != 0) {
foundX = upCentre.x;
foundY = upCentre.y;
break;
}
if(testThePoint(downCentre.x, downCentre.y, map) != 0) {
foundX = downCentre.x;
foundY = downCentre.y;
break;
}
int i;
for(i = 0; i < radius; i++) {
if(leftWithinMap == YES) {
// LEFT Side - stop short of top/bottom rows because up/down horizontal cursors check that line
// if cursor position is within map
if(i < radius - 1) {
if(leftUp.y > 0) {
// check it
if(testThePoint(leftUp.x, leftUp.y, map) != 0) {
foundX = leftUp.x;
foundY = leftUp.y;
break;
}
leftUp.y -= 1; // moving up
}
if(leftDown.y < kIYMapHeight) {
// check it
if(testThePoint(leftDown.x, leftDown.y, map) != 0) {
foundX = leftDown.x;
foundY = leftDown.y;
break;
}
leftDown.y += 1; // moving down
}
}
}
if(rightWithinMap == YES) {
// RIGHT Side
if(i < radius - 1) {
if(rightUp.y > 0) {
if(testThePoint(rightUp.x, rightUp.y, map) != 0) {
foundX = rightUp.x;
foundY = rightUp.y;
break;
}
rightUp.y -= 1; // moving up
}
if(rightDown.y < kIYMapHeight) {
if(testThePoint(rightDown.x, rightDown.y, map) != 0) {
foundX = rightDown.x;
foundY = rightDown.y;
break;
}
rightDown.y += 1; // moving down
}
}
}
if(upWithinMap == YES) {
// UP Side
if(upRight.x < kIYMapWidth) {
if(testThePoint(upRight.x, upRight.y, map) != 0) {
foundX = upRight.x;
foundY = upRight.y;
break;
}
upRight.x += 1; // moving right
}
if(upLeft.x > 0) {
if(testThePoint(upLeft.x, upLeft.y, map) != 0) {
foundX = upLeft.x;
foundY = upLeft.y;
break;
}
upLeft.y -= 1; // moving left
}
}
if(downWithinMap == YES) {
// DOWN Side
if(downRight.x < kIYMapWidth) {
if(testThePoint(downRight.x, downRight.y, map) != 0) {
foundX = downRight.x;
foundY = downRight.y;
break;
}
downRight.x += 1; // moving right
}
if(downLeft.x > 0) {
if(testThePoint(upLeft.x, upLeft.y, map) != 0) {
foundX = downLeft.x;
foundY = downLeft.y;
break;
}
downLeft.y -= 1; // moving left
}
}
}
if(foundX != -1 && foundY != -1) {
break;
}
radius++;
}
// build the return object
if(foundX != -1 && foundY != -1) {
SomeBigObjCStruct *foundPoint = [SomeBigObjCStruct mapPointWithX:foundX Y:foundY levelId:point.levelId];
foundPoint.z = [self zWithLevelId:point.levelId];
return foundPoint;
}
return nil;
}
You can combine many ways to speed it up.
A way to accelerate the pixel lookup is to use what I call a spatial lookup map. It is basically a downsampled map (say of 8x8 pixels, but its a tradeoff) of the pixels in that block. Values can be "no pixels set" "partial pixels set" "all pixels set". This way one read can tell if a block/cell is either full, partially full or empty.
scanning a box/rectangle around the center may not be ideal because there are many pixels/cells which are far far away. I use a circle drawing algorithm (Bresenham) to reduce the overhead.
reading the raw pixel values can happen in horizontal batches, for example a byte (for a cell size of 8x8 or multiples of it), dword or long. This should give you a serious speedup again.
you can also use multiple levels of "spatial lookup maps", its again a tradeoff.
For the distance calculatation the mentioned lookup table can be used, but its a (cache)bandwidth vs calculation speed tradeoff (I dunno how it performs on a GPU for example).
Another approach I have investigated and likely will stick to: Utilizing the Bresenham circle algorithm.
It is surprisingly fast as it saves you any sort of distance comparisons!
You effectively just draw bigger and bigger circles around your target point so that when the first time you encounter a non-black pixel you automatically know it is the closest, saving any further checks.
What I have not verified yet is whether the bresenham circle will catch every single pixel but that wasn't a concern for my case as my pixels will occur in blobs of some sort.
I would do a simple lookup table - for every pixel, precalculate distance to the closest non-black pixel and store the value in the same offset as the corresponding pixel. Of course, this way you will need more memory.

Resources