I'm working on a graphics application where the user can draw any number of Lines (with some thickness from point A to point B), Rectangles, or Ellipses on a canvas.
After they're done, I have a set of shape data indicating the location of each shape and line drawn and I need to determine how many unique pixels they've colored as part of a research project.
My naive algorithm is to implement bool shape.Contains(x,y) for each shape and call it for every drawn shape for every pixel in the image to determine if that pixel was drawn by a line, rectangle or ellipse.
Working the other way, I could create void shape.SetPixels(bool[,] canvas) and have each shape set to true, each pixel it contains. This is what I've actually implemented and with large data sets, it's painfully slow.
I have a feeling that there's a more direct way to go from the raw shape data to the output that I need without examining every pixel. So my question is, given the set of shape data, is there a O(n) function bool[,] IsColored(int x, int y) {} that can generate a matrix of true/falses for colored pixels more directly than either idea I've given?
Avoid the Bitmap.GetPixel method. It is very very slow. If possible your low level access to the bitmap data with LockBits or similar techniques.
In one of my projects I used:
public void LoadFromBitmap(Bitmap bmp)
{
if (bmp.Width != Width || bmp.Height != Height)
throw new ArgumentException("Size missmatch");
unsafe
{
BitmapData bmpData = null;
try
{
bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
for (int y = 0; y < bmpData.Height; y++)
{
uint* p = (uint*)((byte*)bmpData.Scan0 + y * bmpData.Stride);
for (int x = 0; x < bmpData.Width; x++)
{
this[x, y] = RawColor.FromARGB(*p);
p++;
}
}
}
finally
{
if (bmpData != null)
bmp.UnlockBits(bmpData);
}
}
}
https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Image/Pixels.cs
Another optimization is implementing a pool for your pixel containing arrays. Frequently allocating objects on the large-object-heap stresses the gc very much in my experience.
The two methods you're talking about are generally your two main options. Either check each pixel as needed, or build some kind of data structure for fast lookup beforehand.
If you have a large canvas, but only a few shapes (thus, relatively few "on" pixels), then it may be best to just record every pixel that is hit by any shape, in a hash for example.
HashSet<KeyValuePair<int,int>> listOfPixelsHitByAnyShape = new HashSet()
foreach(Shape s in allShapes)
{
s.Draw(listOfPixelsHitByAnyShape); // will update listOfPixelsHitByAnyShape
}
// Now we can easily query if a pixel is set
bool isSet = listOfPixelsHitByAnyShape.Contains(new KeyValuePair(10,99))
This should be fast for lookup, at the expense of memory, and time to build the HashSet.
But it won't be quite as fast as your SetPixels(bool[,] canvas) version, nor using as much memory (in the sparse case we're talking about).
If you aren't using a graphics library, please, please do!
If it's a graphics application presumably you're already drawing what the user inputs. Can't you just query that? You could always draw to 2 different targets, one for the pretty user version, and another for the query (each object a unique colour).
How do you want to deal with overlaps?
How large a dataset are we talking? The rendering should be 'free' as the bitmap could be built as the user is drawing in the application.
If what you've done is really slow you could consider using the GPU to draw and query (possibly using fancy shaders).
Related
I have a project on monogame platform. The purpose of the project is to make the calculation of the viewfactor of geometry put into the platform using the ortographic method. In a basic level, I put a basic cube and camera across from the cube. Here as I look into the cube through the camera, I am required to count the number of pixels of an object seen from a perspective by the ortographic method. I already have a solution but it is very slow. In my solution, I count the number of pixels with a certain color and then divide that number to the total number of pixels on the screen. I have heard of a technique that involves using OcclusionQuery. But I guess I have to do some shader programming in order to use that technique, of which I do not have a clue. Can you guys do some suggestions if there is another technique that is easy to implement and faster than what I recently do or explain how that OcclusionQuery works.Here for example I count the total number of grey pixels then divide it to total screen area
here you can find my code written below;
private void CalculateViewFactor(Color[] data)
{
int objectPixelCount = 0;
var color = new Color();
color.R = data[0].R;
color.G = data[0].G;
color.B = data[0].B;
foreach (Color item in data)
if (item.R != color.R && item.G != color.G && item.B != color.B)
objectPixelCount++;
Console.WriteLine(objectPixelCount);
Console.WriteLine(data.Length);
Console.WriteLine( (float) objectPixelCount / data.Length);
}
due to fact that the color of the first pixel of the screen is also color of the background, I take the RGB values of the first pixel and compare these RGB values to all the other pixels on the screen and count the number of pixels which has a different color from the first pixel.
But as I know that this method is pretty slow, I want to adapt OcclusionQuery into my code. If you could help me, I would be grateful.
This is pretty tricky to do right, and I can only suggest an "alternative", not necessarily more performant or better design-wise approach.
In case you don't really need to know exact number of drawn pixels, you can approximate it. There is a technique called Monte Carlo Integration.
Start off by creating N points on the screen with random coordinates. You check and count colors at these points. Divide the number of points with color of your object by the total number of tested points (that is N). What you get is an approximate ratio of pixels that your object occupies on the final screen. If now you multiply this ratio by the total number of pixels on the screen (that is WidthPx * HeightPx) you'll get an approximate number of pixels occupied by the object.
Advantages:
Select bigger N for more accurate result, select lesser N for better performance
Algorithm is simple, harder to screw it up
Disadvantages:
It's random and never deterministic (you'll get a different result every time)
It's approximate and never exact
You'll need to generate 2 * N random values (two for each of test points), generating random values is a long operation
I'm sure later you'll want to draw textures/shading on the screen, and then this technique won't work as you'll not be able to distinguish pixels of your object and the others. You can still manage a smaller unseen buffer, where you draw the same objects, but without any shading, and each object having same unique color, then you apply Monte Carlo algorithm on it, but of course, that'll cost computing resources.
Do you have any idea how Graphics object using resources?
I am drawing several thousands GraphicsPath objects with latitude longitude coordinate on a Panel. Initially those Graphicspaths has to be zoomed (transformed - 4 matrix transformation actually). Then user can move the map around, zooming, with each action called for repainting the graphics path.
The problem is the whole thing is still responsive when zoom level is around 2000-10000, but when it gets to hundreds of thousands (which is the street level zoom) it takes too long to paint and cause the whole application unresponsive. Check the free memory, still plenty of them. CPU usage is still OK.
How come drawing the same thousands of Graphics Path, with the same 4 matrix transformation each becomes extremely slow when zoom factor were increased? Is the problem in the System.Graphics itself when handling Graphics Path coordinate with large number? Do you guys ever face the same problem?
Sorry good people, for not including the code: so here is chunk of the "slow" code: basicaly the iteration part of _paint method. it runs over 30,000 Graphics path, most are polylines extracted from esri shp files. the coordinates for x are + and y are - and fliped upside down so hence the required matrix transform to be painted on panel. The problem is at low value variable zI, it's much faster than the hi-value variable zI. Hi-value zi means much of the graphics path is outside the painted area. I try to reduce the amount of zi by checking isVisible or by interecting rectangle bound. but that still not fast enough. any ideas?
foreach (GraphicsPath vectorDraw in currentShape.vectorPath)
{
GraphicsPath paintPath = (GraphicsPath)vectorDraw.Clone();
OperationMatrix = new Matrix();
OperationMatrix.Translate(-DisplayPort.X, -DisplayPort.Y);
paintPath.Transform(OperationMatrix);
OperationMatrix = new Matrix();
OperationMatrix.Scale(zI, zI);
paintPath.Transform(OperationMatrix);
OperationMatrix = new Matrix(1, 0, 0, -1, 0, DisplaySize.Height);
paintPath.Transform(OperationMatrix);
OperationMatrix = new Matrix();
OperationMatrix.Translate(ClientGap.Width, -ClientGap.Height);
paintPath.Transform(OperationMatrix);
//if (WiredPort.IsVisible(paintPath.GetBounds())) //Futile attempt
//{
Pen LandBoundariesPen = new Pen(Color.FromArgb(255, 225, 219, 127));
GraphContext.DrawPath(LandBoundariesPen, paintPath); // this is the slowest part. When commented it goes faster.
pathCountX++;
}
Help .... :)
For high performance rendering, directX is perferred over WPF. You can also consider using opengl in C#.
Edit: For tutorial on how to use Open GL in C# via the TAO framework, visit below link:
http://xinyustudio.wordpress.com/2008/12/01/using-opengl-in-c-taoframework/
Simply, I want to set any pixel in an image to white where the brightness is greater than a given threshold. I have written the following code which works suitably fast and accurately for my purposes.
using (Bitmap image = new Bitmap("C:\\temp\\test1.png"))
{
for (int x = 0; x < image.Width; x++)
{
for (int y = 0; y < image.Height; y++)
{
Color c = image.GetPixel(x, y);
float f = c.GetBrightness(); //From 0 (black) to 1 (white)
if (f > 0.1) { image.SetPixel(x, y, Color.White); }
}
}
image.Save("C:\\temp\\test2.png");
}
However, it just feels wrong to have to loop through every pixel one-by-one. Is there an optimized approach to this problem using another .NET Imaging or Graphics approach?
For some special cases (such as power of 2 thresholds), you can work on 32-bit chunks using bitmasking tricks, but I doubt it would buy you very much. The far bigger overhead in the code you provided are the GetPixel and SetPixel calls, which are very inefficient. You can greatly speed up the procedure using the LockBits method and processing the underlying data directly from a byte[].
You might optimize by using proven native algorithm implementations (e.g. OpenCV). Although I'm not an expert on this subject, think you will need to iterate through all pixels anyway. Even scientific papers about thresholding algorithms (which rather focus on finding a good threshold) loop through the whole image, so I don't think you have to feel wrong about.
Anyway, if there's some mathematical approach that works on any image, I would also be interested, but I doubt the existence.
Consider it this way, if you want to know if a pixel is above or below the threshold for all the pixels, you are going to have to visit all the pixels. Any pixel not visited will mean you don't know its value.
There is no way to write this in better than O(n) time, especially if you intend to change values of certain pixels.
I've looked everywhere but there doesn't seem to be a standard (I could see) of how one would go about checking to see if an image is blank. In C#
I have a way of doing this, but would love to know what the correct way is of checking to see if an image is blank, so everyone could also know in the future.
I'm not going to copy paste a bunch of code in, if you want me to, it will be my pleasure, but I just first want to explain how i go about checking to see if an image is blank.
You take a .jpg image, Get the width of it. For example 500 pixels
Then you divide that by 2
giving you 250
Then you check what the colour of every pixel is in the location of (250 width, and i height) (where you iterate thought the hight of the image.
What this then do is only check the middle line of pixels of an image, vertically. It goes though all the pixels checking to see if the colour is anything Except white. I've done this so you wont have to search ALL 500*height of pixels and since you will almost always come across a colour in the middle of the page.
Its working... a bit slow...There must be a better way to do this? You can change it to search 2/3/4 lines vertically to increase your chance to spot a page that's not blank, but that will take even longer.
(Also note, using the size of the image to check if it contains something will not work in this case, since a page with two sentences on and a blank page's size is too close to one another)
After solution has been added.
Resources to help with the implementation and understanding of the solution.
Writing unsafe code - pointers in C
Using Pointers in C#
/unsafe (C# Compiler Options)
Bitmap.LockBits Method (Rectangle, ImageLockMode, PixelFormat)
(Note that on the first website, the stated Pizelformat is actually Pixelformat) - Small error i know, just mentioning, might cause some confusion to some.
After I implemented the method to speed up the pixel hunting, the speed didn't increase that much. So I would think I'm doing something wrong.
Old time = 15.63 for 40 images.
New time = 15.43 for 40 images
I saw with the great article DocMax quoted, that the code "locks" in a set of pixels. (or thats how i understood it)
So what I did is lock in the middle row of pixels of each page. Would that be the right move to do?
private int testPixels(String sourceDir)
{
//iterate through images
string[] fileEntries = Directory.GetFiles(sourceDir).Where(x => x.Contains("JPG")).ToArray();
var q = from string x in Directory.GetFiles(sourceDir)
where x.ToLower().EndsWith(".jpg")
select new FileInfo(x);
int holder = 1;
foreach (var z in q)
{
Bitmap mybm= Bitmap.FromFile(z.FullName) as Bitmap;
int blank = getPixelData2(mybm);
if (blank == 0)
{
holder = 0;
break;
}
}
return holder;
}
And then the class
private unsafe int getPixelData2(Bitmap bm)
{
BitmapData bmd = bm.LockBits(new System.Drawing.Rectangle((bm.Width / 2), 0, 1, bm.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);
int blue;
int green;
int red;
int width = bmd.Width / 2;
for (int y = 0; y < bmd.Height; y++)
{
byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride);
blue = row[width * 3];
green = row[width * 2];
red = row[width * 1];
// Console.WriteLine("Blue= " + blue + " Green= " + green + " Red= " + red);
//Check to see if there is some form of color
if ((blue != 255) || (green != 255) || (red != 255))
{
bm.Dispose();
return 1;
}
}
bm.Dispose();
return 0;
}
If you can tolerate the chance of getting it wrong, the approach seems fine; I have done something very similar in my case, although I always had a visual confirmation to deal with errors.
For the performance, the key open question is how you are getting the pixels to test. If you are using Bitmap.GetPixel, you are bound to have performance problems. (Search for "Bitmap.GetPixel slow" in Google to see lots of discussion.)
Far better performance will come from getting all the pixels at once and then looping over them. I personally like Bob Powell's LockBits discussion for clarity and completeness. With that approach, checking all of the pixels may well be reasonable depending on your performance needs.
If you're using System.Drawing.Bitmap you can speed up things up (substantially), by:
Not using GetPixel to access the pixels, use LockBits and UnlockBits to copy the image bitmap to regular memory. See the examples on the MSDN documentation for usage.
Not calling the Width, Height or Size properties in for loop. Call Size once, store the values in a local variable and use those in the loop.
Notes:
When using System.Drawing.Bitmap your image may be in device memory and accessing it may be time consuming.
I don't remember whether loading an image into a Bitmap already converts it to RGB format as other formats are more difficult to work with, but if that is not the case you can create an RGB Bitmap of the same size as your original image, get it's Graphic object (Graphics.FromImage) and use DrawImage to draw the original image in the RGB bitmap.
Edit: Beat to the punch by DocMax.
In any case for speed you can also try using alternative libraries such as the excellent FreeImage which includes C# wrappers.
Scale the image to 1x1 then check one pixel
new Bitmap(previousImage, new Size(1, 1));
I know how to implement them, but what I don't know is whether to apply the transformation pixel by pixel or is there another way to affect the whole image, using a single call, etc?
AFAIK Get.Set Pixel is very slow. I am not sure if they did it this way.
So if it's the grayscale/desaturate filter as a simple case, how would one write it?
You have to lock the image and then work with the memory, directly bypassing SetPixel method.
See here or even better here.
For examle you can set the blue channel to 255 as follows:
BitmapData bmd=bm.LockBits(new Rectangle(0, 0, 10, 10), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);
int PixelSize=4;
for(int y=0; y<bmd.Height; y++)
{
byte* row=(byte *)bmd.Scan0+(y*bmd.Stride);
for(int x=0; x<bmd.Width; x++)
{
row[x*PixelSize]=255;
}
} // it is copied from the last provided link.
In order to achieve an even faster performance, you may want to check out WPF's Pixel Shader Effects implementation, that works with all .NET WPF visual objects, not just images.
Take a look at an article explaining some basic implementation steps for such an effect:
http://bursjootech.blogspot.com/2008/06/grayscale-effect-pixel-shader-effect-in.html