How are GDI+ functions so fast? - c#

I am trying to recreate very simple GDI+ functions, such as scaling and rotating an image. The reason is that some GDI functions can't be done on multiple threads (I found a work around using processes but didn't want to get into that), and processing thousands of images on one thread wasn't nearly cutting it.
Also my images are grayscale, so a custom function would only have to worry about one value instead of 4.
No matter what kind of function I try to recreate, even when highly optimized, it is always SEVERAL times slower, despite being greatly simplified compared to what GDI is doing (I am operating on a 1D array of bytes, one byte per pixel)
I thought maybe the way I was rotating each point could be the difference, so I took it out completely, and basically had a function that goes through each pixel and just sets it to what it already is, and that was only roughly tied with the speed of GDI, even though GDI was doing an actual rotation and changing 4 different values per pixel.
What makes this possible? Is there a way to match it using your own function?

The GDI+ code is written in C/C++, or possibly even partially in assembly. Some GDI+ calls may use GDI, an old and well optimized API. You will find it difficult to match the performance, even if you know all the pixel manipulation tricks.

I am adding my own answer along with my code to help anyone else who may be looking to do this.
From a combination of pointers and using an approximation of Sine and Cosine instead of calling an outside function for the rotation, I have come pretty darn close to reaching GDI speeds. No outside functions are called at all.
It still takes about 50% more time than GDI, but my earlier implementation took over 10 times longer than GDI. And when you consider multi threading, this method can be 10 times faster than GDI. This function can rotate a 300x400 picture in 3 milliseconds on my machine.
Keep in mind that this is for grayscale images and each byte in the input array represents one pixel.
If you have any ideas to make it faster please share!
private unsafe byte[] rotate(byte[] input, int inputWidth, int inputHeight, int cx, int cy, double angle)
{
byte[] result = new byte[input.Length];
int
tx, ty, ix, iy, x1, y1;
double
px, py, fx, fy, sin, cos, v;
byte a, b;
//Approximate Sine and Cosine of the angle
if (angle < 0)
sin = 1.27323954 * angle + 0.405284735 * angle * angle;
else
sin = 1.27323954 * angle - 0.405284735 * angle * angle;
angle += 1.57079632;
if (angle > 3.14159265)
angle -= 6.28318531;
if (angle < 0)
cos = 1.27323954 * angle + 0.405284735 * angle * angle;
else
cos = 1.27323954 * angle - 0.405284735 * angle * angle;
angle -= 1.57079632;
fixed (byte* pInput = input, pResult = result)
{
byte* pi = pInput;
byte* pr = pResult;
for (int x = 0; x < inputWidth; x++)
for (int y = 0; y < inputHeight; y++)
{
tx = x - cx;
ty = y - cy;
px = tx * cos - ty * sin + cx;
py = tx * sin + ty * cos + cy;
ix = (int)px;
iy = (int)py;
fx = px - ix;
fy = py - iy;
if (ix < inputWidth && iy < inputHeight && ix >= 0 && iy >= 0)
{
//keep in array bounds
x1 = ix + 1;
y1 = iy + 1;
if (x1 >= inputWidth)
x1 = ix;
if (y1 >= inputHeight)
y1 = iy;
//bilinear interpolation using pointers
a = *(pInput + (iy * inputWidth + ix));
b = *(pInput + (y1 * inputWidth + ix));
v = a + ((*(pInput + (iy * inputWidth + x1)) - a) * fx);
pr = (pResult + (y * inputWidth + x));
*pr = (byte)(v + (((b + ((*(pInput + (y1 * inputWidth + x1)) - b) * fx)) - v) * fy));
}
}
}
return result;
}

Related

Parsing LSM6DSL raw values

I'm trying to parse the values given from a device with a LSM6DSL chip (gyroscopic and acc.) and I'm having a hard time parsing the data properly for positioning and angle.
From the vendor I've received the information that the unit is running on a resolution of 2000 for the gyro, 8g for the acc.
I receive the data in bytes that are converted by the following to shorts;
public int[] BufferToMotionData(byte[] buffer, int segments = 2)
{
int[] motionDataArray = new int[segments * 3];
int offset = Constants.BufferSizeImage + Constants.CommandLength;
for (int i = 0; i < 6; i++)
{
motionDataArray[i] = BitConverter.ToInt16(buffer, offset + (i * 2));
if (motionDataArray[i] >= Int16.MaxValue)
motionDataArray[i] -= 65535;
}
return motionDataArray;
}
(Edit; Cleaned up version)
This returns values in the range of (example) 961, -16223, -1635, 664, -269, -597.
According to the spec sheet I'm supposed to multiply each vector with it's corresponding value.. * 70f for gyro, .448f for acc.
From the documentation I understand that for the G forces these are in milliG's and gyro in millidegrees per sec?
// Gyro X,Y,Z
gx = Mathf.Deg2Rad * (motionData[0] * 70f / 1000f);
gy = Mathf.Deg2Rad * (motionData[1] * 70f / 1000f);
gz = Mathf.Deg2Rad * (motionData[2] * 70f / 1000f);
// Acc X,Y,Z
ax = motionData[3] * 0.488f / 1000f;
ay = motionData[4] * 0.488f / 1000f;
az = motionData[5] * 0.488f / 1000f;
Update(gx, gy, gz, ax, ay, az);
Update(..) is Madgwick's quaternion formul, although for velocity I use the acceleration vectors.
G force values that I'm getting at this moment after calculation;
X 0.047824 Y -0.320128 Z 0.006344
X 0.07076 Y -0.2562 Z 0.020008
X 0.099552 Y -0.063928 Z -0.13664
These look awfully low, and if applied as velocity it just runs off in a given direction, I know I'm missing a gravity correct although not entirely sure how to apply this.
I'm under the assumption that I do not need to apply drag to my velocity vector since values should be negated by the acceleration values received?
Anyone with experience with this type of chip and actually applying the values to yaw/pitch/roll (or quaternion) and applying the G forces as linear acceleration.
By looking on existing code on GitHub, it's looks like the sensitivity factor for 8g is 244 µg/digit and not 488 µg/digit as you coded it.
Also it look's like raw values are shifted and are in [-r/2,r/2] instead of [0, r]. So you have to add 500µg or 500µdps to it. (But maybe it's linked to a uint/int issue, anyway are you sure about the endianness?)
See here for acc data and here for gyro data.
Based on that, the code should look likes this:
// Gyro X,Y,Z (in rad/s)
gx = Mathf.Deg2Rad * (motionData[0] * 70000f + 500) / 1000000;
gy = Mathf.Deg2Rad * (motionData[1] * 70000f + 500) / 1000000;
gz = Mathf.Deg2Rad * (motionData[2] * 70000f + 500) / 1000000;
// Acc X,Y,Z (in g)
ax = (motionData[3] * 244f + 500) / 1000000;
ay = (motionData[4] * 244f + 500) / 1000000;
az = (motionData[5] * 244f + 500) / 1000000;
Update(gx, gy, gz, ax, ay, az);

Diagonal sweep and orthogonal distance of a point to diagonal

I have a problem where I'm required to find the maximum number of points that are less than or equal to a given distance D to a line drawn in a two-dimensional Euclidean plane. To solve this I wrote the algorithms that would compute a possible maximum if the line was orthogonal to either the x-axis or the y-axis. My problem is when only a diagonal line would yield the maximum number of points.
Given the constraints that both x and y have a minimum value of -1000000 and maximum of 1000000. I wrote the following algorithm to try and find out the maximum. I don't seem to be getting the right answer. Could someone please guide me on where I am going wrong. I've tried drawing a regression line as well but that used vertical distance which did not work for my purposes. Maybe I'm going all wrong and this problem can be solved as an optimization problem. Anyways' any help with a descent explanation is much appreciated.
// diagonal sweep
for (int degree = 1; degree < 180; degree++) if (degree % 90 != 0)
{
int k = 1, degrees = degree;
double x1 = -1000000, x2 = 1000000;
if (degree > 90 && degree < 180)
{
degrees = 180 - degrees;
k = -1;
}
//slope
double m1 = Math.Tan(Math.PI * degrees * k / 180.0);
//Point A
Point A = new Point(x1, m1 * x1);
//Point B
Point B = new Point(x2, m1 * x2);
for (int i = 0; i < x.Length; i++)
{
//Point P = household that needs power
Point P = new Point(x[i], y[i]);
double normalLength = Math.Sqrt((B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y));
double segmentLength = 1d * Math.Abs((P.X - A.X) * (B.Y - A.Y) - (P.Y - A.Y) * (B.X - A.X)) / normalLength;
if (segmentLength <= D)
tempCnt++;
}
maxConnections = Math.Max(maxConnections, tempCnt);
tempCnt = 0;
}
return maxConnections;
If you want to define this problem as an optimization problem, you should do it as follows, but it doesn't seem to me this optimization problem is solveable efficiently as is.
maximize: x_1 + x_2 + ... + x_n + 0*a + 0*b + 0*c
s.t.
x_i * d(p_i, line(a,b,c))/ MAX_DISTANCE <= 1
x_i is in {0,1}
Explanation:
x_i are inclusion variables - can get a value of 0 / 1 , and it indicates if the point p_i is in the required distance from the line.
a,b,c are the parameters for the line: ax + by + c = 0
The idea is to maximize the sum of included points, such that each included point is in the desired range. This is represented by the constraint, if x_i=0 - there is no restriction on the point p_i, as the constraint is always satisfied. Otherwise, x_i=1, and you need the distance from the line (let it be d) satisfy 1* d/MAX_DISTANCE <= 1 - which is exactly what you want.
Though I don't think there is an optimal efficient solution to this optimization problem, you might want to try some heuristical solutions for this optiization - such as Genetic Algorithms or Hill Climbing
As a side note, my gut says this problem is NP-Complete, though I have no proof for it yet - and will update this part of the answer if I (or someone else) can come up with a reduction/polynomial solution.

How to select by color range?

In my application I have loaded a picture and I want to be able to detect similar colors. So if I select a color I want the application to be able to find all pixels with that same (or almost the same) color. This is what I wrote for a detection system that looks in a vertical direction between the point of the mouse click and the end of the bitmap.
for (int y = mouseY; y < m_bitmap.Height; y++)
{
Color pixel = m_bitmap.GetPixel(mouseX, y);
//check if there is another color
if ((pixel.R > curcolor.R + treshold || pixel.R < curcolor.R - treshold) ||
(pixel.G > curcolor.G + treshold || pixel.G < curcolor.G - treshold) ||
(pixel.B > curcolor.B + treshold || pixel.B < curcolor.B - treshold))
{ //YESSSSS!
if ((y - ytop > minheight)&&(curcolor != Color.White)) //no white, at least 15px height
{
colorlayers.Add(new ColorLayer(curcolor, y - 1, ytop));
}
curcolor = pixel;
ytop = y;
}
}
Would this be the best way? Somehow it looks like it doesn't work too good with yellowish colors.
RGB is a 3D space.
A color far away threshold in all directions is not so similar to original one (and what is similar according to numbers may not be so similar to human beings eyes).
I would make a check using HSL (for example) where hue value as a finite 1D range, just for example:
for (int y = mouseY; y < m_bitmap.Height; y++)
{
Color pixel = m_bitmap.GetPixel(mouseX, y);
if (Math.Abs(color.GetHue() - curcolor.GetHue()) <= threshold)
{
// ...
}
}
Moreover please note that using bitmaps in this way (GetPixel() is terribly slow, take a look to this post to see a - much - faster alternative).
It might be interesting to look at how the magic wand tool in Paint.NET works.
This is how they compare 2 colors:
private static bool CheckColor(ColorBgra a, ColorBgra b, int tolerance)
{
int sum = 0;
int diff;
diff = a.R - b.R;
sum += (1 + diff * diff) * a.A / 256;
diff = a.G - b.G;
sum += (1 + diff * diff) * a.A / 256;
diff = a.B - b.B;
sum += (1 + diff * diff) * a.A / 256;
diff = a.A - b.A;
sum += diff * diff;
return (sum <= tolerance * tolerance * 4);
}
Source
The reason why yellow colors give a problem might be that RGB is not a perceptually uniform colorspace. This means that, given a distance between two points/colors in the colorspace, the perception of this color distance/difference will in general not be the same.
That said, you might want to use another color space, like HSL as suggested by Adriano, or perhaps Lab.
If you want to stick to RGB, I would suggest to calculate the euclidian distance, like this (I think it's simpler):
float distance = Math.sqrt((pixel.R-curcolor.R)^2 + (pixel.G-curcolor.G)^2 + (pixel.B-curcolor.B)^2);
if(distance < threshold)
{
// Do what you have to.
}

Moving the mouse along a diagonal line

What kind of math algorithm could I use to calculate the path to move a mouse? I simply want to have this type of function:
animateMouseDiag(int X, int Y){
//Move mouse 1 step towards goal, for loop most likely, from the current Mouse.Position
Thread.Sleep(1);
}
For example, if I give it animateMouseDiag(100,300), it would move the mouse 100 to the right and 300 down, but diagonally, not right-then-down in an 'L'. Similarly, if I gave it (-50,-200) it would move it to those relative coordinates (50 left and 200 up) along the diagonal path.
Thank you! (By the way, this is an alt account since I feel like an idiot asking about basic high school math on my main. I just can't translate it into programming.)
EDIT: I have come up with this:
public static void animateCursorTo(int toX, int toY)
{
double x0 = Cursor.Position.X;
double y0 = Cursor.Position.Y;
double dx = Math.Abs(toX-x0);
double dy = Math.Abs(toY-y0);
double sx, sy, err, e2;
if (x0 < toX) sx = 1;
else sx = -1;
if (y0 < toY) sy = 1;
else sy = -1;
err = dx-dy;
for(int i=0; i < toX; i++){
//setPixel(x0,y0)
e2 = 2*err;
if (e2 > -dy) {
err = err - dy;
x0 = x0 + sx;
}
if (e2 < dx) {
err = err + dx;
y0 = y0 + sy;
}
Cursor.Position = new Point(Convert.ToInt32(x0),Convert.ToInt32(y0));
}
}
This is Bresenham's line algorithm. Strangely though, the lines don't draw on a set angle. They seem to be gravitating towards the top left of the screen.
Store the position coordinates as floating point values, then you can represent the direction as a unit vector and multiply by a specific speed.
double mag = Math.Sqrt(directionX * directionX + directionY * directionY);
mouseX += (directionX / mag) * speed;
mouseY += (directionY / mag) * speed;

Shorten a line by a number of pixels

I'm drawing a custom diagram of business objects using .NET GDI+. Among other things, the diagram consists of several lines that are connecting the objects.
In a particular scenario, I need to shorten a line by a specific number of pixels, let's say 10 pixels, i.e. find the point on the line that lies 10 pixels before the end point of the line.
Imagine a circle with radius r = 10 pixels, and a line with start point (x1, y1) and end point (x2, y2). The circle is centered at the end point of the line, as in the following illustration.
How do I calculate the point marked with a red circle, i.e. the intersection between circle and line? This would give me the new end point of the line, shortening it by 10 pixels.
Solution
Thank you for your answers from which I was able to put together the following procedure. I named it LengthenLine, since I find it more natural to pass a negative number of pixels if I want the line shortened.
Specifically, I was trying to put together a function that could draw a line with rounded corners, which can be found here.
public void LengthenLine(PointF startPoint, ref PointF endPoint, float pixelCount)
{
if (startPoint.Equals(endPoint))
return; // not a line
double dx = endPoint.X - startPoint.X;
double dy = endPoint.Y - startPoint.Y;
if (dx == 0)
{
// vertical line:
if (endPoint.Y < startPoint.Y)
endPoint.Y -= pixelCount;
else
endPoint.Y += pixelCount;
}
else if (dy == 0)
{
// horizontal line:
if (endPoint.X < startPoint.X)
endPoint.X -= pixelCount;
else
endPoint.X += pixelCount;
}
else
{
// non-horizontal, non-vertical line:
double length = Math.Sqrt(dx * dx + dy * dy);
double scale = (length + pixelCount) / length;
dx *= scale;
dy *= scale;
endPoint.X = startPoint.X + Convert.ToSingle(dx);
endPoint.Y = startPoint.Y + Convert.ToSingle(dy);
}
}
Find the direction vector, i.e. let the position vectors be (using floats) B = (x2, y2) and A = (x1, y1), then AB = B - A. Normalize that vector by dividing by its length ( Math.Sqrt(xx + yy) ). Then multiply the direction vector AB by the original length minus the circle's radius, and add back to the lines starting position:
double dx = x2 - x1;
double dy = y2 - y1;
double length = Math.Sqrt(dx * dx + dy * dy);
if (length > 0)
{
dx /= length;
dy /= length;
}
dx *= length - radius;
dy *= length - radius;
int x3 = (int)(x1 + dx);
int y3 = (int)(y1 + dy);
Edit: Fixed the code, aaand fixed the initial explanation (thought you wanted the line to go out from the circle's center to its perimeter :P)
I'm not sure why you even had to introduce the circle. For a line stretching from (x2,y2) to (x1,y1), you can calculate any point on that line as:
(x2+p*(x1-x2),y2+p*(y1-y2))
where p is the percentage along the line you wish to go.
To calculate the percentage, you just need:
p = r/L
So in your case, (x3,y3) can be calculated as:
(x2+(10/L)*(x1-x2),y2+(10/L)*(y1-y2))
For example, if you have the two points (x2=1,y2=5) and (x1=-6,y1=22), they have a length of sqrt(72 + 172 or 18.38477631 and 10 divided by that is 0.543928293. Putting all those figures into the equation above:
(x2 + (10/l) * (x1-x2) , y2 + (10/l) * (y1-y2))
= (1 + 0.543928293 * (-6- 1) , 5 + 0.543928293 * (22- 5))
= (1 + 0.543928293 * -7 , 5 + 0.543928293 * 17 )
= (x3=-2.807498053,y3=14.24678098)
The distance between (x3,y3) and (x1,y1) is sqrt(3.1925019472 + 7.7532190152) or 8.384776311, a difference of 10 to within one part in a thousand million, and that's only because of rounding errors on my calculator.
You can use similar triangles. For the main triangle, d is the hypotenuses and the extension of r is the vertical line that meets the right angle. Inside the circle you will have a smaller triangle with a hypotenuses of length r.
r/d = (x2-a0)/(x2-x1) = (y2-b0)/(y2-y1)
a0 = x2 + (x2-x1)r/d
b0 = y2 + (y2-y1)r/d

Categories

Resources