Performing multiple image averaging in c# - c#

I wish to calculate an average image from 3 different images of the same size that I have. I know this can be done with ease in matlab..but how do I go about this in c#? Also is there an Aforge.net tool I can use directly for this purpose?

I have found an article on SO which might point you in the right direction. Here is the code (unsafe)
BitmapData srcData = bm.LockBits(
new Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
IntPtr Scan0 = srcData.Scan0;
long[] totals = new long[] {0,0,0};
int width = bm.Width;
int height = bm.Height;
unsafe
{
byte* p = (byte*) (void*) Scan0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
for (int color = 0; color < 3; color++)
{
int idx = (y*stride) + x*4 + color;
totals[color] += p[idx];
}
}
}
}
int avgB = totals[0] / (width*height);
int avgG = totals[1] / (width*height);
int avgR = totals[2] / (width*height);
Here is the link to the article:
How to calculate the average rgb color values of a bitmap

ImageMagick can do this for you very simply - at the command-line you could type this:
convert *.bmp -evaluate-sequence mean output.jpg
You could equally calculate the median (instead of the mean) of a bunch of TIFF files and save in a PNG output file like this:
convert *.tif -evaluate-sequence median output.png
Easy, eh?
There are bindings for C/C++, Perl, PHP and all sorts of other janguage interfaces, including (as kindly pointed out by #dlemstra) the Magick.Net binding available here.
ImageMagick is powerful, free and available here.

Related

C# RGB[,] to picturebox

I have a data that is (2448*2048) 5Mpixel image data, but the picturebox only has (816*683) about 500,000 pixels, so I lowered the pixels and I only need a black and white image, so I used the G value to create the image, but The image I output is shown in the following figure. Which part of my mistake?
public int[,] lowered(int[,] greenar)
{
int[,] Sy = new int[816, 683];
int x = 0;
int y = 0;
for (int i = 1; i < 2448; i += 3)
{
for (int j = 1; j < 2048; j += 3)
{
Sy[x, y] = greenar[i, j];
y++;
}
y = 0;
x++;
}
return Sy;
}
static Bitmap Create(int[,] R, int[,] G, int[,] B)
{
int iWidth = G.GetLength(1);
int iHeight = G.GetLength(0);
Bitmap Result = new Bitmap(iWidth, iHeight,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Rectangle rect = new Rectangle(0, 0, iWidth, iHeight);
System.Drawing.Imaging.BitmapData bmpData = Result.LockBits(rect,
System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
IntPtr iPtr = bmpData.Scan0;
int iStride = bmpData.Stride;
int iBytes = iWidth * iHeight * 3;
byte[] PixelValues = new byte[iBytes];
int iPoint = 0;
for (int i = 0; i < iHeight; i++)
{
for (int j = 0; j < iWidth; j++)
{
int iG = G[i, j];
int iB = G[i, j];
int iR = G[i, j];
PixelValues[iPoint] = Convert.ToByte(iB);
PixelValues[iPoint + 1] = Convert.ToByte(iG);
PixelValues[iPoint + 2] = Convert.ToByte(iR);
iPoint += 3;
}
}
System.Runtime.InteropServices.Marshal.Copy(PixelValues, 0, iPtr, iBytes);
Result.UnlockBits(bmpData);
return Result;
}
https://upload.cc/i1/2018/04/26/WHOXTJ.png
You don't need to downsample your image, you can do it in this way. Set picturebox property BackgroundImageLayout as either zoom or stretch and assign it as:
picturebox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
picturebox.BackgroundImage = bitmap;
System.Windows.Forms.ImageLayout.Zoom will automatically adjust your bitmap to the size of picturebox.
You seem to be constantly mixing up your x and y offsets, which can easily be avoided simply by actually calling your loop variables x and y whenever you loop through image data. Also, image data is generally saved line by line, so your outer loop should be the Y loop going over the height, and the inner loop should process the X coordinates on one line, and should thus loop over the width.
Also, I'm not sure where your original data comes from, but in most of the cases I've seen where the image data is in multidimensional arrays like this, the Y is actually the first index in the array. Your actual image building function also assumes this, since it uses G.GetLength(0) to get the height of the image. But your channel resize function doesn't; it makes a multidimensional array as new int[816, 683], which would be a 683*816 image, not 816*683 as you said. So that certainly seems wrong.
Since you confirmed it to be [x,y], I adapted this solution to use it like that.
That aside, you hardcoded a lot of values in your functions, which is very bad practice. If you know you will reduce the image to 1/3rd by taking only one in three pixels, just give that 3 as parameter.
The reduction code:
public static Int32[,] ResizeChannel(Int32[,] origChannel, Int32 lossfactor)
{
Int32 newWidth = origChannel.GetLength(0) / lossfactor;
Int32 newHeight = origChannel.GetLength(1) / lossfactor;
// to avoid rounding errors
Int32 origHeight = newHeight * lossfactor;
Int32 origWidth = newWidth *lossfactor;
Int32[,] newChannel = new Int32[newWidth, newHeight];
Int32 newX = 0;
Int32 newY = 0;
for (Int32 y = 1; y < origHeight; y += lossfactor)
{
newX = 0;
for (Int32 x = 1; x < origWidth; x += lossfactor)
{
newChannel[newX, newY] = origChannel[x, y];
newX++;
}
newY++;
}
return newChannel;
}
The actual build code, as was remarked by GSerg in the comments, is wrong because you don't take the stride into account. The stride is the actual byte length of each line of pixels, and this is not just width * BytesPerPixel, since it gets rounded up to the next multiple of 4 bytes.
So you need to initialize your array as height * stride, not as height * width * 3, and you need to skip your write offset to the next multiple of the stride whenever you go to a lower Y line, rather than assuming it will just get there automatically because your X processing adds 3 for each pixel. Because it will not get there automatically, unless, by pure coincidence, your image width happens to be a multiple of 4 pixels.
Also, if you only use one channel for this, there is no reason to give it all three channels. Just give a single one.
public static Bitmap CreateGreyImage(Int32[,] greyChannel)
{
Int32 width = greyChannel.GetLength(0);
Int32 height = greyChannel.GetLength(1);
Bitmap result = new Bitmap(width, height, PixelFormat.Format24bppRgb);
Rectangle rect = new Rectangle(0, 0, width, height);
BitmapData bmpData = result.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
Int32 stride = bmpData.Stride;
// stride is the actual line width in bytes.
Int32 bytes = stride * height;
Byte[] pixelValues = new Byte[bytes];
Int32 offset = 0;
for (Int32 y = 0; y < height; y++)
{
Int32 workOffset = offset;
for (Int32 x = 0; x < width; x++)
{
pixelValues[workOffset + 0] = (Byte)greyChannel[x, y];
pixelValues[workOffset + 1] = (Byte)greyChannel[x, y];
pixelValues[workOffset + 2] = (Byte)greyChannel[x, y];
workOffset += 3;
}
// Add stride to get the start offset of the next line
offset += stride;
}
Marshal.Copy(pixelValues, 0, bmpData.Scan0, bytes);
result.UnlockBits(bmpData);
return result;
}
Now, this works as expected if your R, G and B channels are indeed identical, But if they are not, you have to realize there is a difference between reducing the image to grayscale and just building a grey image from the green channel. On a colour image, you will get totally different results if you take the blue or red channel instead.
This was the code I executed for this:
Int32[,] greyar = ResizeChannel(greenar, 3);
Bitmap newbm = CreateGreyImage(greyar);

How to convert from System.Drawing.Bitmap to grayscale, then to array of doubles (Double[,]), then back to grayscale Bitmap?

I need to perform some mathematical operations in photographs, and for that I need the floating point grayscale version of an image (which might come from JPG, PNG or BMP files with various colordepths).
I used to do that in Python using PIL and scipy.ndimage, and it was very straightforward to convert to grayscale with PIL and then to an array of floating-point numbers with numpy, but now I need to do something similar in C#, and I'm confused how to do so.
I have read this very nice tutorial, that seems to be a recurring reference, but that only covers the "convert to grayscale" part, I am not sure how to get an array of doubles from a Bitmap, and then (at some moment) to convert it back to System.Drawing.Bitmap for viewing.
I'm sure there are loads of optimal ways to do this.
As #Groo points out perfectly in the comments section, one could use for instance the LockBits method to write and read pixel colors to and from a Bitmap instance.
Going even further, one could use the graphics card of the computer to do the actual computations.
Furthermore, the method Color ToGrayscaleColor(Color color) which turns a color into its
grayscale version is not optically correct. There is a set of ratios which actually need to be applied to the color component strengths. I just used 1, 1, 1 ratios. That's accceptable for me and probably horrible for an artist or a scientist.
In the comments section, #plinth was very nice to point out to this question you should look at, if you want to make an anatomically correct conversion: Converting RGB to grayscale/intensity
Just wanted to share this really easy to understand and implement solution:
First a little helper to turn a Color into it's grayscale version:
public static Color ToGrayscaleColor(Color color) {
var level = (byte)((color.R + color.G + color.B) / 3);
var result = Color.FromArgb(level, level, level);
return result;
}
Then for the color bitmap to grayscale bitmap conversion:
public static Bitmap ToGrayscale(Bitmap bitmap) {
var result = new Bitmap(bitmap.Width, bitmap.Height);
for (int x = 0; x < bitmap.Width; x++)
for (int y = 0; y < bitmap.Height; y++) {
var grayColor = ToGrayscaleColor(bitmap.GetPixel(x, y));
result.SetPixel(x, y, grayColor);
}
return result;
}
The doubles part is quite easy. The Bitmap object is a memory representation of the actual image which you can use in various operations. The colordepth and image format details are only the concern of loading and saving instances of Bitmap onto streams or files. We needn't care about those at this point:
public static double[,] FromGrayscaleToDoubles(Bitmap bitmap) {
var result = new double[bitmap.Width, bitmap.Height];
for (int x = 0; x < bitmap.Width; x++)
for (int y = 0; y < bitmap.Height; y++)
result[x, y] = (double)bitmap.GetPixel(x, y).R / 255;
return result;
}
And turning a double array back into a grayscale image:
public static Bitmap FromDoublesToGrayscal(double[,] doubles) {
var result = new Bitmap(doubles.GetLength(0), doubles.GetLength(1));
for (int x = 0; x < result.Width; x++)
for (int y = 0; y < result.Height; y++) {
int level = (int)Math.Round(doubles[x, y] * 255);
if (level > 255) level = 255; // just to be sure
if (level < 0) level = 0; // just to be sure
result.SetPixel(x, y, Color.FromArgb(level, level, level));
}
return result;
}
The following lines:
if (level > 255) level = 255; // just to be sure
level < 0) level = 0; // just to be sure
are really there in case you operate on the doubles and you want to allow room for little mistakes.
The final code, based mostly in tips taken from the comments, specifically the LockBits part (blog post here) and the perceptual balancing between R, G and B values (not paramount here, but something to know about):
private double[,] TransformaImagemEmArray(System.Drawing.Bitmap imagem) {
// Transforma a imagem de entrada em um array de doubles
// com os valores grayscale da imagem
BitmapData bitmap_data = imagem.LockBits(new System.Drawing.Rectangle(0,0,_foto_franjas_original.Width,_foto_franjas_original.Height),
ImageLockMode.ReadOnly, _foto_franjas_original.PixelFormat);
int pixelsize = System.Drawing.Image.GetPixelFormatSize(bitmap_data.PixelFormat)/8;
IntPtr pointer = bitmap_data.Scan0;
int nbytes = bitmap_data.Height * bitmap_data.Stride;
byte[] imagebytes = new byte[nbytes];
System.Runtime.InteropServices.Marshal.Copy(pointer, imagebytes, 0, nbytes);
double red;
double green;
double blue;
double gray;
var _grayscale_array = new Double[bitmap_data.Height, bitmap_data.Width];
if (pixelsize >= 3 ) {
for (int I = 0; I < bitmap_data.Height; I++) {
for (int J = 0; J < bitmap_data.Width; J++ ) {
int position = (I * bitmap_data.Stride) + (J * pixelsize);
blue = imagebytes[position];
green = imagebytes[position + 1];
red = imagebytes[position + 2];
gray = 0.299 * red + 0.587 * green + 0.114 * blue;
_grayscale_array[I,J] = gray;
}
}
}
_foto_franjas_original.UnlockBits(bitmap_data);
return _grayscale_array;
}

Use Webcam as ambient light sensor in PC

Is it possible to make the webcam of a pc acting as an ambient light sensor?
I am using .Net 4.5 framework in Windows 8 pro.
Philippe's answer to this question: How to calculate the average rgb color values of a bitmap has code that will calculate the average RGB values of a bitmat image (bm in his code):
BitmapData srcData = bm.LockBits(
new Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
IntPtr Scan0 = dstData.Scan0;
long[] totals = new long[] {0,0,0};
int width = bm.Width;
int height = bm.Height;
unsafe
{
byte* p = (byte*) (void*) Scan0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
for (int color = 0; color < 3; color++)
{
int idx = (y*stride) + x*4 + color;
totals[color] += p[idx];
}
}
}
}
int avgR = totals[0] / (width*height);
int avgG = totals[1] / (width*height);
int avgB = totals[2] / (width*height);
Once you have the average RGB values you can use Color.GetBrightness to determine how light or dark it is. GetBrightness will return a number between 0 and 1, 0 is black, 1 is white. You can use something like this:
Color imageColor = Color.FromARGB(avgR, avgG, avgB);
double brightness = imageColor.GetBrightness();
You could also convert the RGB values to HSL and look at the "L", that may be more accurate, I don't know.

Locate pixel by color return point

I need to locate the points/cordinates (x,y) of the first pixel it finds with the specified color.
I have used the GetPixel() method, but it's a bit too slow and were looking into LockBits. How ever I can't figure out if this actually could solve my problem. Can I return the points for the found pixel using LockBits?
Here is my current code:
public Point FindPixel(Image Screen, Color ColorToFind)
{
Bitmap bit = new Bitmap(Screen);
BitmapData bmpData = bit.LockBits(new Rectangle(0, 0, bit.Width, bit.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format32bppPArgb);
unsafe
{
byte* ptrSrc = (byte*)bmpData.Scan0;
for (int y = 0; y < bmpData.Height; y++)
{
for (int x = 0; x < bmpData.Width; x++)
{
Color c = bit.GetPixel(x, y);
if (c == ColorToFind)
return new Point(x, y);
}
}
}
bit.UnlockBits(bmpData);
return new Point(0, 0);
}
You didn't stop using GetPixel() so you are not ahead. Write it like this instead:
int IntToFind = ColorToFind.ToArgb();
int height = bmpData.Height; // These properties are slow so read them only once
int width = bmpData.Width;
unsafe
{
for (int y = 0; y < height; y++)
{
int* pline = (int*)bmpData.Scan0 + y * bmpData.Stride/4;
for (int x = 0; x < width; x++)
{
if (pline[x] == IntToFind)
return new Point(x, bit.Height - y - 1);
}
}
}
The odd looking Point constructor is necessary because lines are stored upside-down in a bitmap. And don't return new Point(0, 0) on failure, that's a valid pixel.
There are few things wrong with your code:
You are using PixelFormat.Format32bppPArgb - you should use pixel format of the image, if they won't match, all pixels will be copied under the hood anyways.
You are still using GetPixel, so all this hassle will not give you any advantage.
To use LockBits efficiently, you basically want to lock your image and then use unsafe pointers to get values of pixels. Code to do this will vary a bit for different pixel formats, assuming you really will have 32bpp format with blue being on LSB, your code could look like this:
for (int y = 0; y < bmpData.Height; ++y)
{
byte* ptrSrc = (byte*)(bmpData.Scan0 + y * bmpData.Stride);
int* pixelPtr = (int*)ptrSrc;
for (int x = 0; x < bmpData.Width; ++x)
{
Color col = Color.FromArgb(*pixelPtr);
if (col == ColorToFind) return new Point(x, y);
++pixelPtr; //Increate ptr by 4 bytes, because it is int
}
}
Few remarks:
For each line new ptrSrc is computed using Scan0 + stride value. This is because just increasing the pointer might fail, if Stride != bpp * width, which may be the case.
I assumed that blue pixel is represented as LSB, and alpha as MSB, which I think was not the case, because those GDI pixel formats were.. strange ;), just make sure you check it, if it's the other way, inverse bytes before using FromArgb() method.
If your pixel format is 24bpp, it's a bit more tricky, because you can't use int pointer and increase it by 1 (4 bytes), for obvious reasons.

Converting an array of Pixels to an image in C#

I have an array of int pixels in my C# program and I want to convert it into an image. The problem is I am converting Java source code for a program into equivalent C# code. In java the line reads which displays the array of int pixels into image:
Image output = createImage(new MemoryImageSource(width, height, orig, 0, width));
can someone tell me the C# equivalent?
Here orig is the array of int pixels. I searched the Bitmap class and there is a method called SetPixel but the problem is it takes a x,y coordinate number. But what I have in my code is an array of int pixels. Another weird thing is my orig array has negative number and they are way far away from 255. In Java this is the same case (meaning both the array in C# and Java have equivalent value) and the values is working fine in Java.
But I can't get that line translated into C#. Please help.
Using WPF, you can create a bitmap (image) directly from your array. You can then encode this image or display it or play with it:
int width = 200;
int height = 200;
//
// Here is the pixel format of your data, set it to the proper value for your data
//
PixelFormat pf = PixelFormats.Bgr32;
int rawStride = (width * pf.BitsPerPixel + 7) / 8;
//
// Here is your raw data
//
int[] rawImage = new int[rawStride * height / 4];
//
// Create the BitmapSource
//
BitmapSource bitmap = BitmapSource.Create(
width, height,
96, 96, pf, null,
rawImage, rawStride);
You can use Bitmap.LockBits to obtain the bitmap data that you can then manipulate directly, rather than via SetPixel. (How to use LockBits)
I like the WPF option already presented, but here it is using LockBits and Bitmap:
// get the raw image data
int width, height;
int[] data = GetData(out width, out height);
// create a bitmap and manipulate it
Bitmap bmp = new Bitmap(width,height, PixelFormat.Format32bppArgb);
BitmapData bits = bmp.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, bmp.PixelFormat);
unsafe
{
for (int y = 0; y < height; y++)
{
int* row = (int*)((byte*)bits.Scan0 + (y * bits.Stride));
for (int x = 0; x < width; x++)
{
row[x] = data[y * width + x];
}
}
}
bmp.UnlockBits(bits);
With (as test data):
public static int[] GetData(out int width, out int height)
{
// diagonal gradient over a rectangle
width = 127;
height = 128;
int[] data = new int[width * height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
int val = x + y;
data[y * width + x] = 0xFF << 24 | (val << 16) | (val << 8) | val;
}
}
return data;
}
Well, I'm assuming each int is the composite ARGB value? If there isn't an easy option, then LockBits might be worth looking at - it'll be a lot quicker than SetPixel, but is more complex. You'll also have to make sure you know how the int is composed (ARGB? RGBA?). I'll try to see if there is a more obvious option...
MemoryImageSource's constructor's 3rd argument is an array of ints composed of argb values in that order
The example in that page creates such an array by;
pix[index++] = (255 << 24) | (red << 16) | blue;
You need to decompose that integer array to a byte array (shift operator would be useful), but it should be in bgr order, for LockBits method to work.
I would recommend using LockBits but a slower SetPixel based algorithm might look something like
// width - how many int's per row
// array - array of integers
Bitmap createImage(int width, int[] array)
{
int height = array.Length / width;
Bitmap bmp = new Bitmap(width, height);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < array.Length; x += width)
{
bmp.SetPixel(x, y, Color.FromArgb(array[i]));
}
}
return bmp;
}

Categories