Converting a Bitmap image to a matrix - c#

In C#, I need to convert an image that I have already converted to Bitmap in to a matrix of the size of the image's width and height that consists of the uint8 of the Bitmap data. In another word placing the Bitmap data inside of a matrix and converting them to uint8, so I can do the calculations that I am intended to do on the matrix rows and column.

Try something like this:
public Color[][] GetBitMapColorMatrix(string bitmapFilePath)
{
bitmapFilePath = #"C:\9673780.jpg";
Bitmap b1 = new Bitmap(bitmapFilePath);
int hight = b1.Height;
int width = b1.Width;
Color[][] colorMatrix = new Color[width][];
for (int i = 0; i < width; i++)
{
colorMatrix[i] = new Color[hight];
for (int j = 0; j < hight; j++)
{
colorMatrix[i][j] = b1.GetPixel(i, j);
}
}
return colorMatrix;
}

Related

Show an array of bytes as an image on a form

I wrote some code to show an array of bytes as an image. There is an array of bytes in which every element represents a value of 8-bit gray scale image. Zero equals the most black and 255 does the most white pixel. My goal is to convert this w*w-pixel gray-scale image to some thing accepted by pictureBox1.Image.
This is my code:
namespace ShowRawImage
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int i = 0, j = 0, w = 256;
byte[] rawIm = new byte[256 * 256];
for(i = 0; i < w; ++i)
{
for (j = 0; j < w; ++j)
{
rawIm[i * w + j] = (byte)j; // BitConverter.GetBytes(j);
}
}
MemoryStream mStream = new MemoryStream();
mStream.Write(rawIm, 0, Convert.ToInt32(rawIm.Length));
Bitmap bm = new Bitmap(mStream, false);// the error occurs here
mStream.Dispose();
pictureBox1.Image = bm;
}
}
}
However I get this error:
Parameter is not valid.
The error snapshot
where is my mistake?
EDIT:
In next step I am going to display 16-bit grayscale images.
The Bitmap(Stream, bool) constructor expects a stream with an actual image format (eg. PNG, GIF, etc.) along with header, palette, and possibly compressed image data.
To create a Bitmap from raw data, you need to use the Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0) constructor, but that is also quite inconvenient because you need a pinned raw data that you can pass as scan0.
The best if you just create an 8bpp bitmap with grayscale palette and set the pixels manually:
var bmp = new Bitmap(256, 256, PixelFormat.Format8bppIndexed);
// making it grayscale
var palette = bmp.Palette;
for (int i = 0; i < 255; i++)
palette.Entries[i] = Color.FromArgb(i, i, i);
bmp.Palette = palette;
Now you can access its raw content as bytes where 0 is black and 255 is white:
var bitmapData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
for (int y = 0; y < bitmapData.Height; y++)
{
for (int x = 0; x < bitmapData.Width; x++)
{
unsafe
{
((byte*) bitmapData.Scan0)[y * bitmapData.Stride + x] = (byte)x;
}
}
}
bmp.UnlockBits(bitmapData);
The result image:
But if you don't want to use unsafe code, or you want to set pixels by colors, you can use this library (disclaimer: written by me) that supports efficient manipulation regardless of the actual PixelFormat. Using that library the last block can be rewritten like this:
using (IWritableBitmapData bitmapData = bmp.GetWritableBitmapData())
{
IWritableBitmapDataRow row = bitmapData.FirstRow;
do
{
for (int x = 0; x < bitmapData.Width; x++)
row[x] = Color32.FromGray((byte)x); // this works for any pixel format
// row.SetColorIndex(x, x); // for the grayscale 8bpp bitmap created above
} while (row.MoveNextRow());
}
Or like this, using Parallel.For (this works only because in your example all rows are the same so the image is a horizontal gradient):
using (IWritableBitmapData bitmapData = bmp.GetWritableBitmapData())
{
Parallel.For(0, bitmapData.Height, y =>
{
var row = bitmapData[y];
for (int x = 0; x < bitmapData.Width; x++)
row[x] = Color32.FromGray((byte)x); // this works for any pixel format
// row.SetColorIndex(x, x); // for the grayscale 8bpp bitmap created above
});
}
As said in the comments - bitmap is not just an array. So to reach your goal you can create bitmap of needed size and set pixels with Bitmap.SetPixel:
Bitmap bm = new Bitmap(w, w);
for(var i = 0; i < w; ++i)
{
for (var j = 0; j < w; ++j)
{
bm.SetPixel(i,j, Color.FromArgb(j, j, j));
}
}

WPF Display a portion of bigger image in ImageBox

I'm coming from Winforms trying to rewrite a program in WPF, and I want to display a certain portion of the whole image, depending on an Id I use for a list, that I load each portion of the whole image in. I was able to do it successfully in Winforms, but I want to perform the same task in WPF using Controls.Image. Heres what I did in Winforms.
PictureBox picBox;
List<Image> tileImageList;
Image FullImage;
public TileFrame(PictureBox pbox)
{
picBox = pbox;
FullImage = picBox.Image; //The source of the picBox is set to the full image on init
tileImageList = new List<Image>();
PopTileList();
}
void PopTileList()
{
const int SIZE = 32;
Bitmap bitFullImage = new Bitmap(FullImage);
for (int y = 0; y < 48; y++)
{
for (int x = 0; x < 64; x++)
{
var portion = bitFullImage.Clone(new Rectangle((x * SIZE), (y * SIZE), SIZE, SIZE), bitFullImage.PixelFormat);
tileImageList.Add(portion);
}
}
picBox.Image = tileImageList[10];//The first image that shows when this is done
}
public void ShowTilePic(int selectedId)
{
picBox.Image = tileImageList[--selectedId];
}
Since the image being displayed will change based on the selected item of a listbox, the tileImageList is crucial for relating the list box selected index and the tileImageList index. Other answers I've searched for seemed to make it much more complicated than what I've done here. Is there a simple way to do this in WPF and in code?
Nvm I figured it out.
List<CroppedBitmap> tileImageList;
Image imageBox;
public TileFrame(MainWindow mWindow)
{
tileImageList = new List<CroppedBitmap>();
imageBox = mWindow.ImageBox;
PopTileList();
}
void PopTileList()
{
const int SIZE = 32;
var bitmapImage = (BitmapSource)imageBox.Source;
for (int y = 0; y < 48; y++)
{
for (int x = 0; x < 64; x++)
{
var portion = new CroppedBitmap(bitmapImage, new Int32Rect((x * SIZE), (y * SIZE), SIZE, SIZE));
tileImageList.Add(portion);
}
}
}
public void ShowTilePic(int selectedId)
{
imageBox.Source = tileImageList[selectedId];
}

Get grayscale Image height without considering white pixels

I have grayscale pictures of an ArrayList<System.Windows.Controls.Image> laid out horizontally on a Canvas. Their ImageSource are of type System.Windows.Media.Imaging.BitmapImage.
Is there a way to measure in pixels the height of each Image without considering white, non-transparent pixels Outside the colored part ?
Lets say I have an Image of height 10, in which the whole top half is white and the bottom half is black; I would need to get 5 as it's height. In the same way, if that Image had the top third black, middle third white and bottom third black, the height would be 10.
Here's a drawing that shows the desired heights (in blue) of 3 images:
I am willing to use another type for the images, but it Must be possible to either get from a byte[] array to that type, or to convert Image to it.
I have read the docs on Image, ImageSource and Visual, but I really have no clue where to start.
Accessing pixel data from a BitmapImage is a bit of a hassle, but you can construct a WriteableBitmap from the BitmapImage object which is much easier (not to mention more efficient).
WriteableBitmap bmp = new WriteableBitmap(img.Source as BitmapImage);
bmp.Lock();
unsafe
{
int width = bmp.PixelWidth;
int height = bmp.PixelHeight;
byte* ptr = (byte*)bmp.BackBuffer;
int stride = bmp.BackBufferStride;
int bpp = 4; // Assuming Bgra image format
int hms;
for (int y = 0; y < height; y++)
{
hms = y * stride;
for (int x = 0; x < width; x++)
{
int idx = hms + (x * bpp);
byte b = ptr[idx];
byte g = ptr[idx + 1];
byte r = ptr[idx + 2];
byte a = ptr[idx + 3];
// Construct your histogram
}
}
}
bmp.Unlock();
From here, you can construct a histogram from the pixel data, and analyze that histogram to find the boundaries of the non-white pixels in the images.
EDIT: Here's a Silverlight solution:
public static int getNonWhiteHeight(this Image img)
{
WriteableBitmap bmp = new WriteableBitmap(img.Source as BitmapImage);
int topWhiteRowCount = 0;
int width = bmp.PixelWidth;
int height = bmp.PixelHeight;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int pixel = bmp.Pixels[y * width + x];
if (pixel != -1)
{
topWhiteRowCount = y - 1;
goto returnLbl;
}
}
}
returnLbl:
return topWhiteRowCount >= 0 ? height - topWhiteRowCount : height;
}

Partially convert to grayscale using AForge?

Converting a bitmap to grayscale is pretty easy with AForge:
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
// first convert to a grey scale image
var filterGreyScale = new Grayscale(0.2125, 0.7154, 0.0721);
me = filterGreyScale.Apply(me);
return me;
}
But I need something more tricky:
Imagine you want to convert everything to grayscale except for a circle in the middle of the bitmap. In other words: a circle in the middle of the given bitmap should keep its original colours.
Let's assume the radius of the circle is 20px, how should I approach this?
This can be accomplished using MaskedFilter with a mask that defines the circled area you describe. As the documentation states
Mask can be specified as .NET's managed Bitmap, as UnmanagedImage or
as byte array. In the case if mask is specified as image, it must be 8
bpp grayscale image. In all case mask size must be the same as size of
the image to process.
So the mask image has to be generated based on the source image's width and height.
I haven't compiled the following code but it should get you on your way. If the circle is always in the same spot, you could generate the image mask outside the method so that it doesn't have to be regenerated each time you apply the filter. Actually you could have the whole MaskedFilter generated outside the method that applies it if nothing changes but the source image.
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
var radius = 20, x = me.Width / 2, y = me.Height / 2;
using (Bitmap maskImage = new Bitmap(me.Width, me.Height, PixelFormat.Format8bppIndexed))
{
using (Graphics g = Graphics.FromImage(maskImage))
using (Brush b = new SolidBrush(ColorTranslator.FromHtml("#00000000")))
g.FillEllipse(b, x, y, radius, radius);
var maskedFilter = new MaskedFilter(new Grayscale(0.2125, 0.7154, 0.0721), maskImage);
return maskedFilter.Apply(me);
}
}
EDIT
The solution for this turned out to be a lot more trickier than I expected. The main problem was that the MaskedFilter doesn't allow the usage of filters that change the images format, which the Grayscale filter does (it changes the source to an 8bpp or 16 bpp image).
The following is the resulting code, which I have tested, with comments added to each part of the ConvertToGrayScale method explaining the logic behind it. The gray-scaled portion of the image has to be converted back to RGB since the Merge filter doesn't support merging two images with different formats.
static class MaskedImage
{
public static void DrawCircle(byte[,] img, int x, int y, int radius, byte val)
{
int west = Math.Max(0, x - radius),
east = Math.Min(x + radius, img.GetLength(1)),
north = Math.Max(0, y - radius),
south = Math.Min(y + radius, img.GetLength(0));
for (int i = north; i < south; i++)
for (int j = west; j < east; j++)
{
int dx = i - y;
int dy = j - x;
if (Math.Sqrt(dx * dx + dy * dy) < radius)
img[i, j] = val;
}
}
public static void Initialize(byte[,] arr, byte val)
{
for (int i = 0; i < arr.GetLength(0); i++)
for (int j = 0; j < arr.GetLength(1); j++)
arr[i, j] = val;
}
public static void Invert(byte[,] arr)
{
for (int i = 0; i < arr.GetLength(0); i++)
for (int j = 0; j < arr.GetLength(1); j++)
arr[i, j] = (byte)~arr[i, j];
}
public static Bitmap ConvertToGrayScale(this Bitmap me)
{
if (me == null)
return null;
int radius = 20, x = me.Width / 2, y = me.Height / 2;
// Generate a two-dimensional `byte` array that has the same size as the source image, which will be used as the mask.
byte[,] mask = new byte[me.Height, me.Width];
// Initialize all its elements to the value 0xFF (255 in decimal).
Initialize(mask, 0xFF);
// "Draw" a circle in the `byte` array setting the positions inside the circle with the value 0.
DrawCircle(mask, x, y, radius, 0);
var grayFilter = new Grayscale(0.2125, 0.7154, 0.0721);
var rgbFilter = new GrayscaleToRGB();
var maskFilter = new ApplyMask(mask);
// Apply the `Grayscale` filter to everything outside the circle, convert the resulting image back to RGB
Bitmap img = rgbFilter.Apply(grayFilter.Apply(maskFilter.Apply(me)));
// Invert the mask
Invert(mask);
// Get only the cirle in color from the original image
Bitmap circleImg = new ApplyMask(mask).Apply(me);
// Merge both the grayscaled part of the image and the circle in color in a single one.
return new Merge(img).Apply(circleImg);
}
}

showing and saving a 2D-Array of grayscale Pixel

i have a black/white line-sensor wich gives me the grayscale values (0-255) of scanned data. i want to save this values as Bitmap (.bmp) and also show them in a picturebox. my current code looks like this:
PixelFormat px = PixelFormat.Canonical;
Bitmap bitmap = new Bitmap(width, height, px);
int[,] GreyScaleArray2D = new int[width,height];
for (int yy = 0; yy < (lb_SpReceivedData.Count-width); yy += width)
{
for (int xx = 0; xx < width; xx++)
{
GreyScaleArray2D[xx,tmp] = lb_SpReceivedData[(yy+xx)];
}
tmp++;
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int red = GreyScaleArray2D[x,y]; // read from array
int green = GreyScaleArray2D[x,y]; // read from array
int blue = GreyScaleArray2D[x,y]; // read from array
bitmap.SetPixel(x, y, Color.FromArgb(0, red, green, blue));
}
}
pictureBox1.Image = bitmap;
Question) i get an error of invalid parameter if i pass
PixelFormat.Canonical
to the bitmap constructor.
Question) even though i pass the bitmap to my picturebox there is nothing to see in the picturebox! what am i doing wrong?
thanks

Categories