Suppose I have a series of black and white images, NOT grayscale. I'm trying to calculate an average of all images. I have some sample code that should work but I'm wondering if there is a better way?
Bitmap[] Images = ReadAndScale(Width: 50, Height: 50);
int Width = 50;
int Height = 50;
double[,] Result = new int[50,50]
for (int i = 0 i < Images.Count; i++)
{
for (int j = 0; j < Width; j++)
{
for (int k = 0; k < Height; k++)
{
Result[j, k] += Images[i].GetPixel(j,k) == Color.White ? 0
: 1.0 / (double)Images.Count;
}
}
}
At the end of these loops you have an array Result[] that contains the average value; > .5 is black otherwise the average is white.
Well, I think your general algorithm is fine, not really a way to make that "better".
The only way I see you could make it "better" would be to scrape more performance out of it, but do you need that?
The main perf issue I see is the use of GetPixel() as it is a relatively slow method. Here is an example using unsafe code that should run much faster: Unsafe Bitmap
Don't let the word "unsafe" scare you, it just is the keyword for enabling true pointers in C#.
Well, you could make the images the inner loop, and break out as soon as you've guaranteed the average will be less than or greater than .5. Not sure if that is really "better".
Related
I have WPF canvas. And I need to draw up to half million pixels with different color. I've tried to draw pixel-by-pixel, but it was incredibly slow. So I've decided to create Image and draw it. I don't know, if it's the best way how to do it, so if you know better way, tell me.
So my question is, how can I create and draw image to canvas? I've searched, but I wasn't able to find anything.
I have two dimensional array of colors and I need to draw them, probably via an image, so how can I do it?
Thanks, Soptik
EDIT: Now, I use this code, but it takes seconds to draw even 100*100 pixels.
for(int i = 0; i < w; i++)
{
for(int j = 0; j < h; j++)
{
Draw(i, j, Brushes.Aqua);
}
...
private void Draw(int x, int y, SolidColorBrush b)
{
Line l = new Line();
l.Stroke = b;
l.X1 = x;
l.Y1 = y;
l.X2 = x + 1;
l.Y2 = y + 1;
l.StrokeThickness = 1;
canvas.Children.Add(l);
}
Using your current method is not "bad." It might be slow due to the massive size of the 2d array you have, but looping through two for loops is normal for this process. Some potential solutions could be loading each row as a rect onto your Canvas to show the image being processed, but if that is not necessary than I would investigate how to handle the pixel data and possibly processing more than one at time.
This Question is similar to yours and might help
I seek to optimize the performance of my small program, which functionality relies on detecting an image which is most similar to given example. Problem is, the method that I use is really slow and could use a bit of reworking.
I also find that I cannot use Parallel.For to compute the similarity value due to the fact that the function you'll see below is already being called from Parallel.ForEach cycle. Eh.
My similarity method:
public static double isItSame(Bitmap source, Color[,] example)
{
double rez = 0;
for (int x = 20; x < 130; x += 3)
{
for (int y = 10; y < 140; y += 3)
{
Color color1 = source.GetPixel(x, y);
rez += Math.Abs(color1.R - example[x, y].R) + Math.Abs(color1.G - example[x, y].G) + Math.Abs(color1.B - example[x, y].B);
}
}
return rez;
}
Will greatly appreciate any help to optimize this solution. My own way to optimize it was to just do x+3 instead of x++, and same for y, but it results in poor overall results. Eh.
I'm working on a strange project. I have access to a laser cutter that I am using to make stencils (from metal). I can use coordinates to program the machine to cut a certain image, but what I was wondering was: how can I write a program that would take a scanned image that was black and white, and give me the coordinates of the black areas? I don't mind if it gives every pixel even though I need only the outer lines, I can do that part.
I've searched for this for a while, but the question has so many words with lots of results such as colors and pixels, that I find tons of information that isn't relevant. I would like to use C++ or C#, but I can use any language including scripting.
I used GetPixel in C#:
public List<String> GetBlackDots()
{
Color pixelColor;
var list = new st<String>();
for (int y = 0; y < bitmapImage.Height; y++)
{
for (int x = 0; x < bitmapImage.Width; x++)
{
pixelColor = bitmapImage.GetPixel(x, y);
if (pixelColor.R == 0 && pixelColor.G == 0 && pixelColor.B == 0)
list.Add(String.Format("x:{0} y:{1}", x, y));
}
}
return list;
}
If we assume that the scanned image is perfectly white and perfectly black with no in-between colors, then we can just take the image as an array of rgb values and simply scan for 0 values. If the value is 0, it must be black right? However, the image probably won't be perfectly black, so you'll want some wiggle room.
What you do then would look something like this:
for(int i = 0; i < img.width; i++){
for(int j = 0; j < img.height; j++){
// 20 is an arbitrary value and subject to your opinion and need.
if(img[i][j].color <= 20)
//store i and j, those are your pixel location
}
}
Now if you use C#, it'll be easy to import most image formats, stick em in an array, and get your results. But if you want faster results, you'd be better off with C++.
This shortcut relies completely on the image values being very extreme. If large areas of your images are really grey, then the accuracy of this approach is terrible.
While there are many solutions in many languages, I'll outline a simple solution that I would probably use myself. There is a imaging great library for Python called PIL (Python Imaging Library - http://www.pythonware.com/products/pil/) which could accomplish what you need very easily.
Here's an example of something that might help you get started.
image = Image.open("image.png")
datas = image.getdata()
for item in datas:
if item[0] < 255 and item[1] < 255 and item[2] < 255 :
// THIS PIXEL IS NOT WHITE
Of course that will count any pixel that is not completely white, you might want to add some padding so pixels which are not EXACTLY white also get picked up as being white. You'll also have to keep track of which pixel you are currently looking at.
I have a 8 x 8 matrix of floating point numbers and need to calculate eigenvector and eigenvalue from it. This is for feature reduction using PCA (Principal Component Analysis) and is one hell of a time consuming job if done by traditional methods. I tried to use power method as, Y = C*X where X is my 8 X 8 matrix.
float[,] XMatrix = new float[8, 1];
float[,] YMatrix = new float[8, 1];
float max = 0;
XMatrix[0, 0] = 1;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 1; j++)
{
for (int k = 0; k < 8; k++)
{
YMatrix[i, j] += C[i, k] * XMatrix[k, j];
if (YMatrix[i, j] > max)
max = YMatrix[i, j];
}
}
}
I know it is incorrect but cannot figure it out. I need help for using a power method or perhaps more effective way of calculating it.
Thanks in advance.
To retrieve the eigenvalues/eigenvectors in an efficent manner (i.e. fast!) for any size (dense) matrix, is not entirely trivial. I would suggest you use something like the QR algorithm (although this maybe overkill for a one-off calculation of a single 8x8 matrix).
The QR algorithm computes a Schur decomposition of a matrix. It is certainly one of the
most important algorithm in eigenvalue computations. However, it is applied to dense matrices only (as stated above).
The QR algorithm consists of two separate stages. First, by means of a similarity
transformation, the original matrix is transformed in a finite number of steps to Hessenberg
form or – in the Hermitian/symmetric case – to real tridiagonal form. This first stage of
the algorithm prepares its second stage, the actual QR iterations that are applied to the
Hessenberg or tridiagonal matrix.
The overall complexity (number of floating points) of the algorithm is O(n3). For a good explanation of this algorithm see here. Or searches for eigenvalue algorithm in Google should provide you with many alternative ways of calculating your required eigenvalues/vectors.
Also, I have not looked into this in detail, but Math.NET a free library may help you here...
I wrote function:
public static byte[, ,] Bitmap2Byte(Bitmap image)
{
int h = image.Height;
int w = image.Width;
byte[, ,] result= new byte[w, h, 3];
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
Color c= image.GetPixel(i, j);
result[i, j, 0] = c.R;
result[i, j, 1] = c.G;
result[i, j, 2] = c.B;
}
}
return result;
}
But it takes almost 6 seconds to convert 1800x1800 image. Can I do this faster?
EDIT:
OK, I found this: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx
There is nice example. Only question I have is about Marshal.Copy. Can I make it copy data directly into byte[,,] ?
EDIT 2:
OK, sometimes I got strange values of pixels and they do not seem to follow r0 g0 b0 r1 g1 b1 rule. Why? Never mind. Figured it out.
EDIT 3:
Made it. 0,13s vs 5,35s :)
You can speed this up considerably by using a BitmapData object which is returned from Bitmap.LockBits. Google "C# Bitmap LockBits" for a bunch of examples.
GetPixel is painfully, painfully slow, making it (ironically) completely unsuitable for the manipulation of individual pixels.
I've been wondering this for a while.
In .NET 4.0 Microsoft introduced the Parallel library. Basically what this does is there is a Parallel.For method that will automatically spawn off numerous threads to help with the work.
For instance if you originally had a For(int i =0;i<3;i++){ code...}, A parallel.For loop would probably create 3 threads and each thread would have a different value for i running through the inner code. So the best thing i can suggest is a Parallel.For loop with a
Color c
lock(obraz)
{
c = obraz.GetPixel(..)
}
...
when getting the pixel.
If you need any more explanation on parallelism I can't really assist you before you take some time to study it as it is a huge area of study.
I just tried parallel For.
It doesn't work without SyncLock on the bitmap.
It says the object is in use.
So it pretty much just works, in serial lol... what a mess.
For xx As Integer = 0 To 319
pq.ForAll(Sub(yy)
Dim Depth = getDepthValue(Image, xx, yy) / 2047
Dim NewColor = Depth * 128
Dim Pixel = Color.FromArgb(NewColor, NewColor, NewColor)
SyncLock Bmp2
Bmp2.SetPixel(xx, yy, Pixel)
End SyncLock
End Sub)
Next
In case you're wondering, this is converting kinect's depth map -> bitmap.
Kinect's depth range is from 11bit(0-2047) and represents distance not color.