Edit: Oh shit, everyone replied while I was typing D: Type slower D:
soulcyon, on 16 November 2011 - 02:06 PM, said:
D: just ingenius... how do they make these algorithms?
I haven't gone through the whitepaper of how the algorithm works, but I'm going to guess the meat of it is nothing more than a simple recursive function. I'll use a maze as my example:
How would you write a program to solve that? Here's how I'd do it: This maze is on a grid, like someone took graph paper and just blacked out some of the lines for walls. If you step into the left entrance, for example, your legal moves would be UP and RIGHT. If you stepped right, your legal moves would be LEFT and RIGHT. Only, in this case, we're going to eliminate "LEFT" as a legal move since we just came from there.
Make sense? Using that thinking, this maze can be solved by one function:
const UP = 0;
const RIGHT = 1;
const DOWN = 2;
const LEFT = 3;
// Maze is an object that knows what the legal moves are
// for every position, and where the exit is.
// It also tracks where we're "standing" and where we've
// been.
function solveMaze($maze) {
if ($maze->solved())
return $maze->getPath();
for ($i = 0; $i < 4; $i++) {
if (!$maze->directionBlocked($i) && !$maze->beenThereBefore($i)) {
$success = solveMaze($maze->stepTo($i));
if ($success)
return $success;
}
}
return false;
}
That's it. Not including the explanatory constants and comments, that's all of about 10 lines. Basically what's going on is, that function has no effing idea how to solve a maze. All it knows how to do is check to see if this space is the exit. If not, it will attempt moving to an empty space that 1) is not blocked, and 2) it's never been in before, and then it calls itself to start the whole damn process over again. So in all of 10 lines of code, we're attempting every path from a given start point to a given end point, and only returning the one that actually works.
Now let's think of how we might use the same concept to find a seam in this image. We're only concerned with one type of path: any path that starts along one edge of the picture, ends along the opposite edge of the picture, and doesn't intersect any areas of high "energy".
So step one is going to have nothing to do with what we just did. Step one is going to be running a filter similar to "Find Edges" in photoshop. If you were writing it yourself, you'd probably look at each pixel (or a small box of pixels) and determine if the pixels/units surrounding it are within a similar color range, or if they're drastically different. If they're drastically different, chances are that you're on the edge of a new element in the photograph, and you mark that section as being high "energy". And you'd probably put a value with that. If you're going from stark white to stark black, that's be a really really really high energy. If you're going from a dark green to a dark blue, that'd be a lower energy. If you're going from one color to the exact same color, that's no energy. You run over every pixel or group of pixels in the photo, and construct a "grid". If every pixel were a space on a piece of graph paper, each line would have your "energy" level on it, defining how big the difference is between those blocks.
...so... piece of graph paper ... an object that describes which lines can and can't be crossed ... is this sounding familiar to you? ;-)
We can use
that exact friggin same algorithm to find a "seam", with just a few really really basic tweaks. Obviously, if we're drawing a seam from left to right, you wouldn't want the algorithm to be able to move right, then up, then left-- so you'd just need to bar it from moving toward the edge you started on. From there, you just need to try going in your remaining three directions. Your first version might always choose to move along the path of least resistance -- cross the "line" with the least amount of energy on it. Your second version might try going in all of the directions, even if one path makes you cross more energy than another, because the path as a whole might result in less energy. Your third version might be a combination of the two, defining a range of energy levels that are OK to cross, but not attempting to cross any really significant energy lines. The point is, no matter how you roll it, the function should add up each amount of "energy" it crossed and return the lowest energy path it could find.
Since our function requires that we give it a starting point, we'd just try it with all the pixels (or every few pixels) across one edge of the image. Take the path that had the least amount of energy, and bam. You, sir, just found a seam of pixels that you can safely delete from this image.
Run that algorithm repeatedly until the image is resized to where you want it.
Done. You could write an acceptable version of sucker in a couple days, after some research on the best way to manipulate image data.
This would be very very slow, of course -- no doubt these guys found a way to optimize it way more than what I'm suggesting. But I'd bet a lot of money that it's the same basic logic.