I am trying to "fake 3D" in a game in WPF. Think of a road, and that the objects appear somewhere in the distant. As they get closer, they look bigger, and eventually they grow in size very fast.
I'm thinking that when the object appears, it's close to 0 in width and height. As it moves towards the player, it becomes closer to hundred percent of its true size.
I think I will need to solve this using logarithmic calculations, and there are several threads on that. What I would really want to do however, is to send in three values to a LogaritmicGrowth method:
the starting Y point
the point at which the object should appear at 100%
the y point where the object is at this very moment.
Thus, what I would like to get in return is the scaling factor for the object in question. So if it's halfway between the starting point and the ending point, then perhaps 0.3 (or so) should be returned.
I can write the method inputs and outputs myself, but need help with the calculation. Thanks!
I am not entirely sure about the use of log here. This is a simple geometry problem.
Think about a point P which is D distance in front of you, which has a height Y (from your line of observation). Your screen is d distance in front of you. The intersection point of the light from P on the screen is p, which makes a height y on screen.
Then, by considering the similar triangles, one can show that:
y = (Y/D) d
Just in case someone else is looking at this question in the future, here's the correct reply (I figured it out myself):
/// <summary>
/// Method that enlargens the kind of object sent in
/// </summary>
public void ExponentialGrowth2(string name, float startY, float endY)
{
float totalDistance = endY - startY;
float currentY = 0;
for (int i = 0; i < Bodies.Bodylist.Count; i++)
{
if (Bodies.Bodylist[i].Name.StartsWith(name)) //looks for all bodies of this type
{
currentY = Bodies.Bodylist[i].PosY;
float distance = currentY - startY + (float)Bodies.Bodylist[i].circle.Height;
float fraction = distance / totalDistance; //such as 0.8
Bodies.Bodylist[i].circle.Width = Bodies.Bodylist[i].OriginalWidth * Math.Pow(fraction, 3);
Bodies.Bodylist[i].circle.Height = Bodies.Bodylist[i].OriginalHeight * Math.Pow(fraction, 3);
}
}
}
The method could be worked on further, such as allowing randomized power-to values (say from 1.5 to 4.5). Note that the higher the exponential value, the greater the effect.
Related
I've managed to put together a procedural terrain with defined regions and I am looking to procedurally place objects within the world within these regions. The regions are defined by their height and I am trying to utilise this to correctly place certain objects in certain regions however my result seems to come out slightly odd where objects are able to spawn outside the defined region height. I am using an AnimationCurve as a mesh height curve to prevent water areas from becoming terrain like. I am unsure if this is causing the issue behind in the correct placement. Would appreciate any insight into where I might be going wrong
Defined regions:
The Rock region is defined with a height of 0.7 and I try to spawn trees on the map only at a Rock location
Spawning object (Spawn 10) at rock location
int amount = 0;
for (int y = 0; y < mapHeight; y++)
{
if(amount < 10)
{
for (int x = 0; x < mapWidth; x++)
{
float currentHeight = noiseMap[x, y];
if(currentHeight.ToString("f1") == (0.7f).ToString())
{
Debug.Log(currentHeight.ToString("f1"));
Vector3 spawnPosition = new Vector3(Random.Range((x), (mapWidth / 2)), currentHeight, Random.Range(y, (mapHeight / 2)));
var block = Instantiate(AssetsToSpawn[0].AssetPrefab, spawnPosition, Quaternion.identity);
block.transform.SetParent(this.transform);
amount++;
break;
}
}
} else
{
return;
}
Result
Some seem to spawn in the right location albeit looking slightly weird but the one on the far left is finding itself on flat land, with water and sand; an area not defined as 0.7 or Rock type.
I think the issue lies in the line
Vector3 spawnPosition = new Vector3(Random.Range((x), (mapWidth / 2)), currentHeight, Random.Range(y, (mapHeight / 2)));
you seem to already iterate your map grid using x and y so why pick random positions on your map that might be anywhere between this current position and the center of the map?
I think you would rather want a random position within the current field and do e.g.
Vector3 spawnPosition = new Vector3(x + Random.Range(-0.5f, 0.5f), currentHeight, y + Random.Range(-0.5f, 0.5f));
Besides that why go through strings in
if(currentHeight.ToString("f1") == (0.7f).ToString())
I see that it's probably for the rounding but I would still prefer to rather do e.g.
if(Mathf.Abs(currentHeight - 0.7f) <= 0.05f)
which would have about the same effect but the threshold is better to control.
However, sounds to me like rock rather would be anything between 0.49 and 0.7 actually so actually it should be
if(currentHeight > 0.49f && currentHeight <= 0.7f)
Finally, unless you store somewhere which map position you already populated with a tree your outer for loop will always over and over enter at the exact same grid position, the first one that is encountered to fulfill your inner loop's condition!
So far you where always using the exact se position for all 10 trees, only the random position caused that it didn't seem so.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I'm new in game development and I'm stuck in a problem.
I would like to know the new player position each seconds, here an example :
The player start at (2.5;2.5) and he go to (6.5;3.8).
His velocity is by example 2 units per seconds, and I would like to know the player position after 1sec. So something like this :
What I would like to know it's A every seconds but I don't know at all how I can do that...
I hope you will be able to help me, thanks in advance !
His velocity is by example 2 units per seconds.
I assume, that 'unit' means 'vector of length = 1'.
First of all, you need to calculate the AB vector (movement vector):
mov_vec = [xb-xa, yb-ya] = [6.5 - 2.5, 3.8 - 2.5] = [4, 1.3]
So, we know, that total unit did movement by [4, 1.3]. We need to normalize this vector. Normalized vector (unit vector) 'norm_mov_vec' will be codirectional with 'mov_vec', but it's length will be 1. See this link if you want to know more abut unit vectors.
Compute the length of movement vector:
mov_vec_len = sqrt( 4^2 + 1.3^2 ) ~= 4.2059
Compute normalized vector:
norm_mov_vec = [4/4.2059, 1.3/4.2059] ~= [0.9510, 0.3090]
And that's it. 'norm_mov_vec' is your 'unit-movement-vector', so if player is moving in that direction with speed of N units per second, you can very easily compute it's position after T seconds:
pos_after_T_sec_with_speed_N_units_per_sec = start_pos + ( N * T * norm_mov_vec )
EDIT:
Sample code, using Vector2 type from XNA. Can't test it, but I hope you will get the idea:
//In your case:
//start_pos = 'A' point
//end_pos = 'B' point
//time = number of seconds that elapsed
//speed = number of units per second
Vector2 calculatePosition(ref Vector2 start_pos, ref Vector2 end_pos, Uint32 time, Uint32 speed)
{
Vector2 mov_vec = Vector2.Substract(end_pos, start_pos);
Vector2 norm_mov_vec = Vector2.Normalize(mov_vec);
Vector2 delta_vec = norm_mov_vec * time * speed;
return Vector2.Add(start_pos, delta_vec);
}
First you need to work out the total distance covered, that's your vector. A vector is a movement, not two points in space.
Then you just divide each dimension, x and y in this case, by the time taken to do the move in unit of measurement (seconds) to get the distance per second.
Then you multiply each x and y by the number of seconds from 0, i.e. 1 second in your example, to get the position after 1 second.
I don't know what's available to you in your framework or libraries but a good Vector class will be so helpful, you'll want to be able to do math on the vector instance directly, such as:
Point origin = sprite.Position; // Assumes some sprite object with a position.
Point dest = new Point(200,344); // Destination.
Vector totalTranslation = new Vector(dest.X - origin.X, dest.Y - origin.Y);
Vector perSecond = totalTranslation / 60; // assuming takes a minute to move.
Vector distanceMoved = perSecond * 4; // distance moved after 4 seconds.
Point newPosition = new Point(origin.X + distanceMoved.X, origin.Y + distanceMoved.Y);
sprite.Position = newPosition; // Or using some orchestration class...
spriteManager.Move(sprite, newPosition); // ...like this.
Note being able to divide a vector directly. Else you have to divide each spatial dimension of the vector and make a new vector, or make a helper class to do it.
In real life, you might want to calculate based on milliseconds. I wouldn't use a fixed frame counter since it could look juddery, but work everything out based on a timer.
As I say, a good library or immutable Vector struct/class is the key here. Then its a case of thinking about the problem on graph paper.
Also, build up a palette of small functions you can chain together to do cooler, bigger stuff.
Another interesting problem is using an easing function to work out a coordinate after a given time to achieve the effect of a sprite slowing down as it 'lands'.
This is not programming, but vector math mostly, but anyway:
Your player is moving along the vector BA ( Point B minus Point A ) which is
Direction Vector: ( 4.0 / 1.3 )
This vector has a length of:
SquareRoot(4.0 * 4.0 + 1.3 * 1.3) = 4.2
A vector of the same direction and length of one unit would therefore be the vector with both components divided by the length of 4.2:
Direction Vector of length 1: (0.95 / 0.30)
As your player is fast and moves two units, it would be double length:
Direction Vector of length 2: (1.90 / 0.60)
Now each tick, add 1.90 and 0.60 respectively to your player coordinates, until they equal (roughly) the target coordinates.
x-displacement: 6.5-2.5 = 4
y-displacement: 3.8-2.5 = 1.3
Math.sqrt((4n)(4n)+(1.3n)(1.3n)) = 2
n=2/Math.sqrt(17.69)
x-displacement/second = 4n = 8/Math.sqrt(17.69) = 1.90207
y-displacement/second = 1.3n = 2.6/Math.sqrt(17.69) = 0.61817
so after get these values, it is really easy to calculate the position each second
You can use (as a general solution) these simple trigonometry formulae:
x = A.x + v * cos(fi) * t;
y = B.y + v * sin(fi) * t;
fi = atan2(B.y - A.y, B.x - A.x);
sample solution
// Since there's no common 2d Point double based type,
// let (x, y) point be represented as Tuple<Double, Double>
// where Item1 is x, and Item2 is y
public static Tuple<Double, Double> Move(Tuple<Double, Double> fromPoint,
Tuple<Double, Double> toPoint,
Double velocity,
Double time) {
Double fi = Math.Atan2(toPoint.Item2 - fromPoint.Item2, toPoint.Item1 - fromPoint.Item1);
return new Tuple<Double, Double>(
fromPoint.Item1 + velocity * Math.Cos(fi) * time,
fromPoint.Item2 + velocity * Math.Sin(fi) * time);
}
...
for (int t = 0; t < 10; ++t) {
Tuple<Double, Double> position =
Move(new Tuple<Double, Double>(2.5, 2.5),
new Tuple<Double, Double>(6.5, 3.8),
2.0,
t);
Console.Write("t = ");
Console.Write(t);
Console.Write(" x = ");
Console.Write(position.Item1);
Console.Write(" y = ");
Console.Write(position.Item2);
Console.WriteLine();
}
Ok so I have searched and searched for a solution to my problem, but non seem to fix it.
I need to make a game with a rotating "cannon", my cannon is a simple rectangle placed in the middle of my panel that I can rotate with my keyboard. It rotates around one edge. I want to shoot out of the edge on the other side. I have found the starting point of where to shoot my bullets by using:
x = a + dia * (float)Math.Cos(angle);
y = b + dia * (float)Math.Sin(angle)
where "a, b" is the center coordinate I rotate it around and "dia" is the diagonal of the rectangle and "angle" is the angle of the one half of my rectangle.
public float rotate = 0.0f;
g.TranslateTransform(a , b);
g.RotateTransform(rotate);
I have a own class for my bullets that I put in a List.
So far so good. But when I rotate my cannon, the bullets don't come out from the tip anymore..they just start appearing far off where I want them to. it's because of this code:
x = (float)((x * Math.Cos(rotate)) - (y * Math.Sin(rotate)));
y = (float)((x * Math.Sin(rotate)) + (y * Math.Cos(rotate)));
that's supposed to update the x, y coordinates of the tip of the cannon.
If I delete it, it just fires from the same spot(no shit).
Can someone please explain to me what code I need to write to update the X, Y so they come out of my rectangle edge? It's driving me crazy..
Edit:
Found my answer staring at the screen in the early mornings. I had no need for any "find new x, y coordinates". I simply made a updater that updated the original angle with the float number it needed to move a little bit each time i rotated it.
hah! so simple, yet so hard to see.
First of all,
x = (float)((x * Math.Cos(rotate)) - (y * Math.Sin(rotate)));
y = (float)((x * Math.Sin(rotate)) + (y * Math.Cos(rotate)));
needs to be something like:
float oldx = x;
float oldy = y;
x = (float)((oldx * Math.Cos(rotate)) - (oldy * Math.Sin(rotate)));
y = (float)((oldx * Math.Sin(rotate)) + (oldy * Math.Cos(rotate)));
your new values need to be based purely off the old values..
If there's any other problem after fixing this, it may be related to how the rectangle is translated on the plane.
Edit: If this were a code review, I'd say the solution I just gave isn't quite the best solution either (it just doesn't suffer from the bug you introduced by using the new value of x to calculate the new value of y). See, Math.Cos and Math.Sin are generally expensive operations compared to multiplication and addition. If you had a bunch of points that need transformed the same way, best to calculate Math.Sin(rotate) and Math.Cos(rotate) once and use those values for every point. This might be a good place to use the Flyweight pattern and define a class where an instance would hold all your points for a given object/context so that operations can be done in aggregate.
How can i calulate a valid range (RED) for my object's (BLACK) traveling direction (GREEN). The green is a Vector2 where x and y range is -1 to 1.
What I'm trying to do here is to create rocket fuel burn effekt. So what i got is
rocket speed (float)
rocket direction (Vector2 x = [-1, 1], y = [-1, 1])
I may think that rocket speed does not matter as fuel burn effect (particle) is created on position with its own speed.
A cheap and cheerful trick with 2D vectors is to transpose the x and y, then flip the sign on one of them to get the perpendicular vector (pseudo code):
Vector2 perpendicular ( -original.y, original.x ) // Or original.y, -original.x
Then you could do something like:
direction + perpendicular * rand(-0.3 , 0.3)
Update: having realised the question asks for the opposite vector (too busy looking at the picture!) I figure I had better answer that too. Multiply 'direction' by -1 to get the opposite vector. So this:
perpendicular * rand(-0.3 , 0.3) - direction
should give you a random direction vector somewhere in your range (not normalised, but close enough for these purposes). Then you can multiply that result by a random number depending on how long you want the tail.
If to expend upon OlduwanSteve's answer, you can make is such that it's somewhat physically accurate.
You want to create several vectors that will represent the expulsion (the red lines).
First define the number of vectors you want to represent the expulsion with - lets mark it n.
You want to get a set of n numbers which sum up to Vx. These numbers will be the x components of the expulsion vectors. You can do this like so (semi-pseudo code):
SumX = Vx;
for (i = 0; i < n; i++)
{
Ax[i] = -rand(0..SumX); // Ax is the array of all expulsion vectors x components
SumX -= Ax[i];
}
Now you'll want to calculate Ay (the y components of the expulsion vectors). This is quite similar to calculating the, except that SumY = 0.
Here instead of splitting up SumY among n elements, you need to decide a maximal y component. Best way I can think of to select this is to define a maximal allowed angle for the expulsion vectors and define the maximal Vy using: maxVy = minVx*tan(maxAlpha).
Now you can get Ay using this (semi-pseudo code):
SumY = maxVy*2; // The actual range is (-maxVy, maxVy), but using (0, 2*maxVy) is simpler IMO
for (i = 0; i < n; i++)
{
Ay[i] = rand(0..SumY);
SumY -= Ay[i];
}
for (i = 0; i < n; i++)
{
Ay[i] -= maxVy; // Translate the range back to (-maxVy, maxVy) from (0, 2*maxVy)
}
Now you have arrays of both the x and y components of the expulsion vectors. Iterate over both arrays and pair up elements to create the vectors (you don't have to iterate both arrays in the same order).
Notes:
• I align the axes in my calculations such that X is parallel to the objects speed vector (the green line).
• The calculation for maxVy does NOT guarantee that a vector of angle maxAlpha will be produced, it only guarantees that no vector of larger angle will be.
• The lines Ay[i] = rand(0..SumY) and Ax[i] = -rand(0..SumX) may lead to vectors with components of size 0. This may lead to annoying scenarios, I'd recommend to handle away such cases (for instance "while rand returns zero, call it again").
I'm trying to write a program to programmatically determine the tilt or angle of rotation in an arbitrary image.
Images have the following properties:
Consist of dark text on a light background
Occasionally contain horizontal or vertical lines which only intersect at 90 degree angles.
Skewed between -45 and 45 degrees.
See this image as a reference (its been skewed 2.8 degrees).
So far, I've come up with this strategy: Draw a route from left to right, always selecting the nearest white pixel. Presumably, the route from left to right will prefer to follow the path between lines of text along the tilt of the image.
Here's my code:
private bool IsWhite(Color c) { return c.GetBrightness() >= 0.5 || c == Color.Transparent; }
private bool IsBlack(Color c) { return !IsWhite(c); }
private double ToDegrees(decimal slope) { return (180.0 / Math.PI) * Math.Atan(Convert.ToDouble(slope)); }
private void GetSkew(Bitmap image, out double minSkew, out double maxSkew)
{
decimal minSlope = 0.0M;
decimal maxSlope = 0.0M;
for (int start_y = 0; start_y < image.Height; start_y++)
{
int end_y = start_y;
for (int x = 1; x < image.Width; x++)
{
int above_y = Math.Max(end_y - 1, 0);
int below_y = Math.Min(end_y + 1, image.Height - 1);
Color center = image.GetPixel(x, end_y);
Color above = image.GetPixel(x, above_y);
Color below = image.GetPixel(x, below_y);
if (IsWhite(center)) { /* no change to end_y */ }
else if (IsWhite(above) && IsBlack(below)) { end_y = above_y; }
else if (IsBlack(above) && IsWhite(below)) { end_y = below_y; }
}
decimal slope = (Convert.ToDecimal(start_y) - Convert.ToDecimal(end_y)) / Convert.ToDecimal(image.Width);
minSlope = Math.Min(minSlope, slope);
maxSlope = Math.Max(maxSlope, slope);
}
minSkew = ToDegrees(minSlope);
maxSkew = ToDegrees(maxSlope);
}
This works well on some images, not so well on others, and its slow.
Is there a more efficient, more reliable way to determine the tilt of an image?
I've made some modifications to my code, and it certainly runs a lot faster, but its not very accurate.
I've made the following improvements:
Using Vinko's suggestion, I avoid GetPixel in favor of working with bytes directly, now the code runs at the speed I needed.
My original code simply used "IsBlack" and "IsWhite", but this isn't granular enough. The original code traces the following paths through the image:
http://img43.imageshack.us/img43/1545/tilted3degtextoriginalw.gif
Note that a number of paths pass through the text. By comparing my center, above, and below paths to the actual brightness value and selecting the brightest pixel. Basically I'm treating the bitmap as a heightmap, and the path from left to right follows the contours of the image, resulting a better path:
http://img10.imageshack.us/img10/5807/tilted3degtextbrightnes.gif
As suggested by Toaomalkster, a Gaussian blur smooths out the height map, I get even better results:
http://img197.imageshack.us/img197/742/tilted3degtextblurredwi.gif
Since this is just prototype code, I blurred the image using GIMP, I did not write my own blur function.
The selected path is pretty good for a greedy algorithm.
As Toaomalkster suggested, choosing the min/max slope is naive. A simple linear regression provides a better approximation of the slope of a path. Additionally, I should cut a path short once I run off the edge of the image, otherwise the path will hug the top of the image and give an incorrect slope.
Code
private double ToDegrees(double slope) { return (180.0 / Math.PI) * Math.Atan(slope); }
private double GetSkew(Bitmap image)
{
BrightnessWrapper wrapper = new BrightnessWrapper(image);
LinkedList<double> slopes = new LinkedList<double>();
for (int y = 0; y < wrapper.Height; y++)
{
int endY = y;
long sumOfX = 0;
long sumOfY = y;
long sumOfXY = 0;
long sumOfXX = 0;
int itemsInSet = 1;
for (int x = 1; x < wrapper.Width; x++)
{
int aboveY = endY - 1;
int belowY = endY + 1;
if (aboveY < 0 || belowY >= wrapper.Height)
{
break;
}
int center = wrapper.GetBrightness(x, endY);
int above = wrapper.GetBrightness(x, aboveY);
int below = wrapper.GetBrightness(x, belowY);
if (center >= above && center >= below) { /* no change to endY */ }
else if (above >= center && above >= below) { endY = aboveY; }
else if (below >= center && below >= above) { endY = belowY; }
itemsInSet++;
sumOfX += x;
sumOfY += endY;
sumOfXX += (x * x);
sumOfXY += (x * endY);
}
// least squares slope = (NΣ(XY) - (ΣX)(ΣY)) / (NΣ(X^2) - (ΣX)^2), where N = elements in set
if (itemsInSet > image.Width / 2) // path covers at least half of the image
{
decimal sumOfX_d = Convert.ToDecimal(sumOfX);
decimal sumOfY_d = Convert.ToDecimal(sumOfY);
decimal sumOfXY_d = Convert.ToDecimal(sumOfXY);
decimal sumOfXX_d = Convert.ToDecimal(sumOfXX);
decimal itemsInSet_d = Convert.ToDecimal(itemsInSet);
decimal slope =
((itemsInSet_d * sumOfXY) - (sumOfX_d * sumOfY_d))
/
((itemsInSet_d * sumOfXX_d) - (sumOfX_d * sumOfX_d));
slopes.AddLast(Convert.ToDouble(slope));
}
}
double mean = slopes.Average();
double sumOfSquares = slopes.Sum(d => Math.Pow(d - mean, 2));
double stddev = Math.Sqrt(sumOfSquares / (slopes.Count - 1));
// select items within 1 standard deviation of the mean
var testSample = slopes.Where(x => Math.Abs(x - mean) <= stddev);
return ToDegrees(testSample.Average());
}
class BrightnessWrapper
{
byte[] rgbValues;
int stride;
public int Height { get; private set; }
public int Width { get; private set; }
public BrightnessWrapper(Bitmap bmp)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect,
System.Drawing.Imaging.ImageLockMode.ReadOnly,
bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmpData.Stride * bmp.Height;
this.rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr,
rgbValues, 0, bytes);
this.Height = bmp.Height;
this.Width = bmp.Width;
this.stride = bmpData.Stride;
}
public int GetBrightness(int x, int y)
{
int position = (y * this.stride) + (x * 3);
int b = rgbValues[position];
int g = rgbValues[position + 1];
int r = rgbValues[position + 2];
return (r + r + b + g + g + g) / 6;
}
}
The code is good, but not great. Large amounts of whitespace cause the program to draw relatively flat line, resulting in a slope near 0, causing the code to underestimate the actual tilt of the image.
There is no appreciable difference in the accuracy of the tilt by selecting random sample points vs sampling all points, because the ratio of "flat" paths selected by random sampling is the same as the ratio of "flat" paths in the entire image.
GetPixel is slow. You can get an order of magnitude speed up using the approach listed here.
If text is left (right) aligned you can determine the slope by measuring the distance between the left (right) edge of the image and the first dark pixel in two random places and calculate the slope from that. Additional measurements would lower the error while taking additional time.
First I must say I like the idea. But I've never had to do this before and I'm not sure what all to suggest to improve reliability. The first thing I can think of this is this idea of throwing out statistical anomalies. If the slope suddenly changes sharply then you know you've found a white section of the image that dips into the edge skewing (no pun intended) your results. So you'd want to throw that stuff out somehow.
But from a performance standpoint there are a number of optimizations you could make which may add up.
Namely, I'd change this snippet from your inner loop from this:
Color center = image.GetPixel(x, end_y);
Color above = image.GetPixel(x, above_y);
Color below = image.GetPixel(x, below_y);
if (IsWhite(center)) { /* no change to end_y */ }
else if (IsWhite(above) && IsBlack(below)) { end_y = above_y; }
else if (IsBlack(above) && IsWhite(below)) { end_y = below_y; }
To this:
Color center = image.GetPixel(x, end_y);
if (IsWhite(center)) { /* no change to end_y */ }
else
{
Color above = image.GetPixel(x, above_y);
Color below = image.GetPixel(x, below_y);
if (IsWhite(above) && IsBlack(below)) { end_y = above_y; }
else if (IsBlack(above) && IsWhite(below)) { end_y = below_y; }
}
It's the same effect but should drastically reduce the number of calls to GetPixel.
Also consider putting the values that don't change into variables before the madness begins. Things like image.Height and image.Width have a slight overhead every time you call them. So store those values in your own variables before the loops begin. The thing I always tell myself when dealing with nested loops is to optimize everything inside the most inner loop at the expense of everything else.
Also... as Vinko Vrsalovic suggested, you may look at his GetPixel alternative for yet another boost in speed.
At first glance, your code looks overly naive.
Which explains why it doesn't always work.
I like the approach Steve Wortham suggested,
but it might run into problems if you have background images.
Another approach that often helps with images is to blur them first.
If you blur your example image enough, each line of text will end up
as a blurry smooth line. You then apply some sort of algorithm to
basically do a regression analisys. There's lots of ways to do
that, and lots of examples on the net.
Edge detection might be useful, or it might cause more problems that its worth.
By the way, a gaussian blur can be implemented very efficiently if you search hard enough for the code. Otherwise, I'm sure there's lots of libraries available.
Haven't done much of that lately so don't have any links on hand.
But a search for Image Processing library will get you good results.
I'm assuming you're enjoying the fun of solving this, so not much in actual implementation detalis here.
Measuring the angle of every line seems like overkill, especially given the performance of GetPixel.
I wonder if you would have better performance luck by looking for a white triangle in the upper-left or upper-right corner (depending on the slant direction) and measuring the angle of the hypotenuse. All text should follow the same angle on the page, and the upper-left corner of a page won't get tricked by the descenders or whitespace of content above it.
Another tip to consider: rather than blurring, work within a greatly-reduced resolution. That will give you both the smoother data you need, and fewer GetPixel calls.
For example, I made a blank page detection routine once in .NET for faxed TIFF files that simply resampled the entire page to a single pixel and tested the value for a threshold value of white.
What are your constraints in terms of time?
The Hough transform is a very effective mechanism for determining the skew angle of an image. It can be costly in time, but if you're going to use Gaussian blur, you're already burning a pile of CPU time. There are also other ways to accelerate the Hough transform that involve creative image sampling.
Your latest output is confusing me a little.
When you superimposed the blue lines on the source image, did you offset it a bit? It looks like the blue lines are about 5 pixels above the centre of the text.
Not sure about that offset, but you definitely have a problem with the derived line "drifting" away at the wrong angle. It seems to have too strong a bias towards producing a horizontal line.
I wonder if increasing your mask window from 3 pixels (centre, one above, one below) to 5 might improve this (two above, two below). You'll also get this effect if you follow richardtallent's suggestion and resample the image smaller.
Very cool path finding application.
I wonder if this other approach would help or hurt with your particular data set.
Assume a black and white image:
Project all black pixels to the right (EAST). This should give a result of a one dimensional array with a size of IMAGE_HEIGHT. Call the array CANVAS.
As you project all the pixels EAST, keep track numerically of how many pixels project into each bin of CANVAS.
Rotate the image an arbitrary number of degrees and re-project.
Pick the result that gives the highest peaks and lowest valleys for values in CANVAS.
I imagine this will not work well if in fact you have to account for a real -45 -> +45 degrees of tilt. If the actual number is smaller(?+/- 10 degrees), this might be a pretty good strategy. Once you have an intial result, you could consider re-running with a smaller increment of degrees to fine tune the answer. I might therefore try to write this with a function that accepted a float degree_tick as a parm so I could run both a coarse and fine pass (or a spectrum of coarseness or fineness) with the same code.
This might be computationally expensive. To optimize, you might consider selecting just a portion of the image to project-test-rotate-repeat on.