How do you create a 1 bit per pixel mask from an image using GDI in C#? The image I am trying to create the mask from is held in a System.Drawing.Graphics object.
I have seen examples that use Get/SetPixel in a loop, which are too slow. The method that interests me is one that uses only BitBlits, like this. I just can't get it to work in C#, any help is much appreciated.
Try this:
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
...
public static Bitmap BitmapTo1Bpp(Bitmap img) {
int w = img.Width;
int h = img.Height;
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
for (int y = 0; y < h; y++) {
byte[] scan = new byte[(w + 7) / 8];
for (int x = 0; x < w; x++) {
Color c = img.GetPixel(x, y);
if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte)(0x80 >> (x % 8));
}
Marshal.Copy(scan, 0, (IntPtr)((int)data.Scan0 + data.Stride * y), scan.Length);
}
bmp.UnlockBits(data);
return bmp;
}
GetPixel() is slow, you can speed it up with an unsafe byte*.
In the Win32 C API the process to create a mono mask is simple.
Create an uninitialzied 1bpp bitmap as big as the source bitmap.
Select it into a DC.
Select the source bitmap into a DC.
SetBkColor on the destination DC to match the mask color of the source bitmap.
BitBlt the source onto the destination using SRC_COPY.
For bonus points its then usually desirable to blit the mask back onto the source bitmap (using SRC_AND) to zero out the mask color there.
Do you mean LockBits? Bob Powell has an overview of LockBits here; this should provide access to the RGB values, to do what you need. You might also want to look at ColorMatrix, like so.
Related
I have to convert image into black and white, which is captured by using Mobile camera.
I had read questions and answers related to converting image into black and white, but provided solution won't help me.
Below are my image which I had captured.
So I have to save above image in my application folder by converting it into black and white as per of requirement.
I had tried below c# codes but it gives me incomplete image.
Code 1
Bitmap bmp = new Bitmap(#"c:\test.jpg");
Bitmap bw = bmp.Clone(new Rectangle(0, 0, bmp.Width, bmp.Height),
PixelFormat.Format8bppIndexed);
Code 2
Bitmap bmp = new Bitmap(#"c:\test.jpg");
int width = bmp.Width;
int height = bmp.Height;
int[] arr = new int[225];
int i = 0;
Color p;
//Grayscale
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
p = bmp.GetPixel(x, y);
int a = p.A;
int r = p.R;
int g = p.G;
int b = p.B;
int avg = (r + g + b) / 3;
avg = avg < 128 ? 0 : 255; // Converting gray pixels to either pure black or pure white
bmp.SetPixel(x, y, Color.FromArgb(a, avg, avg, avg));
}
}
But both code are converting original image looks like below.
It's may be due to shadowing while capturing image using mobile.
Pls let me know how can I convert this image into black and white without lose of image.
Is there any library which help me anything.
Reduce the threshold to 90 here avg = avg < 90 ? 0 : 255;
or you can use EmguCV, it's much more faster and easier, and they both give the same result.
Image<Gray, byte> img = new Image<Gray, byte>("1.jpg");
img._ThresholdBinary(new Gray(90), new Gray(255));
My professor kind of "challenged me" to create an application that draws pixel by pixel an image converted in Bitmap, where it's data is saved in some sort of binary that I can't wrap my head around.
Here's the example given to me:
const byte image[]={
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
ect ect ect
Now, if the byte data type saves numbers that go from 0 to 255, how is this possible? In the sample code that I was given, there is also the use of "Word" data type but in my IDE it seems like it doesn't exist.
I already wrote the code that converts any image given in input into a bitmap:
FileStream fs = new FileStream(openFileDialog1.FileName, FileMode.Open, FileAccess.Read); //Path is image location
Byte[] bindata = new byte[Convert.ToInt32(fs.Length)];
fs.Read(bindata, 0, Convert.ToInt32(fs.Length));
Bitmap bmp;
using (var ms = new MemoryStream(bindata))
{
bmp = new Bitmap(ms);
}
pictureBox1.Image = bmp; //For now, I just display the converted image on screen
Now I suppose that the next step is to draw the image byte per byte, but I can't get my head around this binary thing and the word data type.. Any kind of help is appreciated :)
if you just want to draw a bitmap pixel at a time, you can do something like this:
Bitmap b = new Bitmap(10, 10);
b.SetPixel(0, 0, Color.Black);
b.SetPixel(1, 3, Color.Red);
pictureBox1.Image = b;
You can just copy your bytes to the Bitmap's memory buffer itself.
BitmapData bufferData = buffer.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
bufferData.SetPixel(x, y, CELL_DEAD);
buffer.UnlockBits(bufferData);
//////////
public static unsafe void SetPixel(BitmapData data, int x, int y, byte pixel)
{
*((byte*)data.Scan0 + y * data.Stride + x) = pixel;
}
I've used it as unsafe but you can play your magic with IntPtr. Of course, you must play your own with width-height synchronization.
UPD: set PixelFormat with care. PixelFormat.Format8bppIndexed is what you need if your colors are in default 256-color palette or you want to define your own palette.
**
How to make 'GetPixel2' work for finding the color at a point
**
So I have a bitmap with lots of single colored shapes.
I have a list of x,y points for those shapes. Then, a second list
with the expected color at those points.
Finally have an algorithm using bitmap.Getpixel and SetPixel working.
Which was definitely slow.
http://csharpexamples.com/fast-image-processing-c/
Suggests using direct memory access to solve this. I'd like to use their sample without looping through the entire image, and hit a single x,y point.
Bitmap bmp2 = (Bitmap)Bitmap.FromFile(Environment.CurrentDirectory + #"\Content\map\provinces.bmp");
BitmapData bitmapData = bmp2.LockBits(new System.Drawing.Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadWrite, bmp2.PixelFormat);
int bytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(bmp2.PixelFormat) / 8;
int heightInPixels = bitmapData.Height;
int widthInBytes = bitmapData.Width * bytesPerPixel;
System.Drawing.Point pt = new System.Drawing.Point((int)provpos2[0].X, (int)provpos2[0].Y);
System.Drawing.Color targetColor = System.Drawing.Color.FromArgb(255, provcolors[0].R, provcolors[0].G, provcolors[0].B);
if (!ColorMatch(GetPixel2(pt.X, pt.Y, bytesPerPixel, bitmapData), targetColor)){
// This hits the completely wrong area.
}
public System.Drawing.Color GetPixel2(int x, int y, int bytesPerPixel, BitmapData bitmapData)
{
unsafe
{
byte* ptrFirstPixel = (byte*)bitmapData.Scan0;
byte* currentLine = ptrFirstPixel + (y * bitmapData.Stride);
x = x + bytesPerPixel;
System.Drawing.Color a = System.Drawing.Color.FromArgb(255, currentLine[x + 2], currentLine[x + 1], currentLine[x]);
return a;
}
}
public static bool ColorMatch(System.Drawing.Color a,System.Drawing.Color b)
{
return (a.ToArgb() & 0xffffff) == (b.ToArgb() & 0xffffff);
}
bytesPerPixel comes out at 3. Tried changing it to 4 just hits another undesired location on the bitmap.
It seems to hit around 1023x,351y instead of the desired 3084x,319y on a 5632x2048 bitmap.
Not entirely sure why it doesnt workout fo you, but keep in mind this:
Bits per pixel comes from colour format used there are a few formats some are handier then others, and sometimes you need to convert them to a strict RGB format. ea 8 bits per colour channel, there also exists RGBA, and there is RGB in bitwise 565 notation as used in some camera's, and there is 24bits per colour. Some formats are not supported in winforms, but are supported in wpf based applications, like 16bit gray formats. (since wpf is more new age like design friendly)
maybe try this it works great for me:
http://www.codeproject.com/Tips/240428/Work-with-bitmap-faster-with-Csharp?msg=5136670
if its 565 maybe do something like
private Bitmap Convert565bppTo24bpp(Bitmap ConvertMe)
{
Bitmap clone = new Bitmap(ConvertMe.Width, ConvertMe.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);//.Format32bppPArgb);
using (Graphics gr = Graphics.FromImage(clone))
{ gr.DrawImage(ConvertMe, new Rectangle(0, 0, clone.Width, clone.Height)); }
return clone;
}
MSDN reference: [1] http://msdn.microsoft.com/en-us/library/5ey6h79d.aspx#Y1178
From the link it says that the first argument will "specifies the portion of the Bitmap to lock" which I set to be a smaller part of the Bitmap (Bitmap is 500x500, my rectangle is (0,0,50,50)) however the returned BitmapData has stride of 1500 (=500*3) so basically every scan will still scan through the whole picture horizontally. However, what I want is only the top left 50x50 part of the bitmap.
How does this work out?
The stride will always be of the full bitmap, but the Scan0 property will be different according to the start point of the lock rectangle, as well as the Height and Width of the BitmapData.
The reason for that is that you will still need to know the real bit-width of the bitmap, in order to iterate over the rows (add stride to address).
A simple way to go about it would be:
var bitmap = new Bitmap(100, 100);
var data = bitmap.LockBits(new Rectangle(0, 0, 10, 10),
ImageLockMode.ReadWrite,
bitmap.PixelFormat);
var pt = (byte*)data.Scan0;
var bpp = data.Stride / bitmap.Width;
for (var y = 0; y < data.Height; y++)
{
// This is why real scan-width is important to have!
var row = pt + (y * data.Stride);
for (var x = 0; x < data.Width; x++)
{
var pixel = row + x * bpp;
for (var bit = 0; bit < bpp; bit++)
{
var pixelComponent = pixel[bit];
}
}
}
bitmap.UnlockBits(data);
So it is basically really just locking the whole bitmap, but giving you a pointer to the top-left pixel of the rectangle in the bitmap, and setting the scan's width and height appropriately.
I am opening different types of images like (8 and 16 bit) and they are (Monocrome, RGB ,palette color).
I have raw pixel data of these images.
I create bitmap like this for 8 bit images.
//for monocrome images i am passing PixelFormat.Format8bppIndexed.
//for RGB images i am passing PixelFormat.Format24bppRgb
PixelFormat format = PixelFormat.Format8bppIndexed;
Bitmap bmp = new Bitmap(Img_Width, Img_Height,format);
Rectangle rect = new Rectangle(0, 0, Img_Width, Img_Height);
//locking the bitmap on memory
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, format);
// copy the managed byte array to the bitmap's image data
Marshal.Copy(rawPixel, 0, bmpData.Scan0, rawPixel.Length);
bmp.UnlockBits(bmpData);
The problem is that when i draw that bmp image then it differs in color than original.
So is there any way to apply lut (lookup table) on that colored images.
i want any unsafe code because i tried getixel and setPixel and they are very slow.
I also dont want Image.fromSource() methods.
Take a look at the bitmap's Image.Palette property.
As far as I know, .NET does not support ICC color profiles, so if you for instance open an image that is using the AdobeRGB color profile, the colours will appear a bit duller and more "greyish" than if you open the same image file in, say, Photoshop or another color-profile-aware software.
This post discusses some color profile issues; you may find something of interest there.
GetPixel and SetPixel are indeed very slow. If you want to do operations on single pixels, consider using a FastBitmap. It allows to quickly color pixels. Using this unsafe bitmap will greatly improve your speed.
i solved this problem see how.
Somewhere i read that GDI+ return BGR value not the RGB.
So i reverse the order and amazing everything fine.
But it is little bit slow.
PixelFormat format = PixelFormat.Format8bppIndexed;
Bitmap bmp = new Bitmap(Img_Width, Img_Height,format);
Rectangle rect = new Rectangle(0, 0, Img_Width, Img_Height);
//locking the bitmap on memory
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, format);
Marshal.Copy(rawPixel, 0, bmpData.Scan0, rawPixel.Length);
int stride = bmpData.Stride;
System.IntPtr Scan0 = bmpData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - bmp.Width * SAMPLES_PER_PIXEL ;
byte red, green, blue;
for (int y = 0; y < bmp.Height; ++y)
{
for (int x = 0; x < bmp.Width; ++x)
{
blue = p[0];
green = p[1];
red = p[2];
p[0] = red;
p[1] = green;
p[2] = blue;
p += 3;
}
p += nOffset;
}
}
////unlockimg the bitmap
bmp.UnlockBits(bmpData);
thanks
Can anybody is having some faster code than that.