i need to read getpixel of bmp with speed but is very low
i used of LockBits
private void LockUnlockBitsExample(Bitmap bmp)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);
}
and this function
private Color GetMyPixel(byte[] rgbValues,Bitmap bmp, int x,int y )
{
int index= (bmp.Width*y+x)*3;
Color MyColor = Color.FromArgb(rgbValues[index], rgbValues[index + 1], rgbValues[index + 2]);
return MyColor;
}
but output of my function is different from original getpixel
In this line:
int index= (bmp.Width*y+x)*3;
I believe that bmp.Stride must be used instead of bmp.Width. Also check assumption that PixelFormat is 24 bits per pixel.
Another thing are color indexes: blue is first (index), then green (index+1), then red (index + 2).
I have code in VB for some reason that does almost the exact same thing as you, so I hope this helps. You can try the following modification to GetMyPixel:
Use Stride instead of Width and invert the byte order in your call to FromArgb.
private Color GetMyPixel(byte[] rgbValues,Bitmap bmp, int x,int y )
{
int index= (bmp.Stride*y+x*3);
if (index > rgbValues.Length - 3)
index = rgbValues.Length - 3;
Color MyColor = Color.FromArgb(rgbValues[index+2], rgbValues[index + 1], rgbValues[index]);
return MyColor;
}
You should check this post out :working with lockbits
it helped me a lot when i did something similar
Related
We are using a camera that acquires up to 60 frames per second, providing Bitmaps for us to use in our codebase.
As our wpf-app requires, these bitmaps are scaled based on a scaling factor; That scaling-process is by far the most limiting factor when it comes to actually displaying 60 fps. I am aware of new Bitmap(Bitmap source, int width, int height) which is obviously the simplest way to resize a Bitmap;
Nevertheless, I am trying to implement a "manual" approach using BitmapData and pointers. I have come up with the following:
public static Bitmap /*myMoBetta*/ResizeBitmap(this Bitmap bmp, double scaleFactor)
{
int desiredWidth = (int)(bmp.Width * scaleFactor),
desiredHeight = (int)(bmp.Height * scaleFactor);
var scaled = new Bitmap(desiredWidth, desiredHeight, bmp.PixelFormat);
int formatSize = (int)Math.Ceiling(Image.GetPixelFormatSize(bmp.PixelFormat)/8.0);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
BitmapData scaledData = scaled.LockBits(new Rectangle(0, 0, scaled.Width, scaled.Height), ImageLockMode.WriteOnly, scaled.PixelFormat);
unsafe
{
var srcPtr = (byte*)bmpData.Scan0.ToPointer();
var destPtr = (byte*)scaledData.Scan0.ToPointer();
int scaledDataSize = scaledData.Stride * scaledData.Height;
int nextPixel = (int)(1 / scaleFactor)*formatSize;
Parallel.For(0, scaledDataSize - formatSize,
i =>
{
for (int j = 0; j < formatSize; j++)
{
destPtr[i + j] = srcPtr[i * nextPixel + j];
}
});
}
bmp.UnlockBits(bmpData);
bmp.Dispose();
scaled.UnlockBits(scaledData);
return scaled;
}
Given scalingFactor < 1.
Actually using this algorithm does not seem to work, though. How are the bits of each pixel arranged in memory, exactly? My guess was that calling Image.GetPixelFormatSize() and deviding its result by 8 returns the number of bytes per pixel; But continuing to copy only formatSize amout of bytes every 1 / scaleFactor * formatSize byte results in a corrupted image.
What am I missing?
After some more research I bumped into OpenCV that has it's own .NET implementation with Emgu.CV, containing relevant methods for faster resizing.
My ResizeBitmap()-function has shrinked significantly:
public static Bitmap ResizeBitmap(this Bitmap bmp, int width, int height)
{
var desiredSize = new Size(width, height);
var src = new Emgu.CV.Image<Rgb, byte>(bmp);
var dest = new Emgu.CV.Image<Rgb, byte>(desiredSize);
Emgu.CV.CvInvoke.Resize(src, dest, desiredSize);
bmp.Dispose();
src.Dispose();
return dest.ToBitmap();
}
I have not tested performance thouroughly, but while debugging, this implementation reduced executiontime from 22ms with new Bitmap(source, width, height) to about 7ms.
This question already has answers here:
Split PNG into RGB and Alpha Channels
(2 answers)
Closed 3 years ago.
I'm using this code to save a bitmap as binary data.
Bitmap bmp = new Bitmap(screenWidth, position);
Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(screenLeft, screenTop, 0, 0, bmp.Size);
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);
File.WriteAllBytes(filename, bmp);
g.Dispose();
As I only need the first channel's values, is it possible to retrieve that from the bitmap? Performance is essential.
You're almost there, but there are a few key details missing:
Instead of using bmp.PixelFormat, force the pixel format for the BitmapData object to PixelFormat.Format32BppArgb, then you're 100% sure what structure you will get, and in 32-bit mode, the stride will always exactly match a predictable width * 4. If you don't do this, you may get unexpected results if the read image happens to be paletted or some sort of 16bpp format where each pixel can't be divided into simple colour component bytes.
Loop over the data and extract the channel. The order of the letters 'ARGB' refers to the a hexadecimal value 0xAARRGGBB (like, for example, 0xFF428ED0), which is a little-endian Uint32 value, meaning the actual order of the colour component bytes is the reverse: { BB, GG, RR, AA }.
So, to extract your channel:
// Channels are: B=0, G=1, R=2, A=3
Int32 channel = 1 // for this example, extract the Green channel.
Int32 width;
Int32 height;
Byte[] rgbaValues;
using (Bitmap bmp = new Bitmap(screenWidth, position))
using (Graphics g = Graphics.FromImage(bmp))
{
width = bmp.Width
height = bmp.Height;
g.CopyFromScreen(screenLeft, screenTop, 0, 0, bmp.Size);
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Int32 bytes = bmpData.Stride * bmp.Height;
rgbaValues = new byte[bytes];
Marshal.Copy(bmpData.Scan0, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);
g.Dispose();
}
Byte[] channelValues = new byte[width * height];
Int32 lineStart = 0;
Int32 lineStartChannel = 0;
for (Int32 y = 0; y < height; ++y)
{
Int32 offset = lineStart;
Int32 offsetChannel = lineStartChannel;
for (Int32 x = 0; x < width; ++x)
{
// For reference:
//Byte blue = rgbaValues[offset + 0];
//Byte green = rgbaValues[offset + 1];
//Byte red = rgbaValues[offset + 2];
//Byte alpha = rgbaValues[offset + 3];
channelValues[offsetChannel] = rgbaValues[offset + channel];
offset += 4;
offsetChannel++;
}
lineStart += stride;
lineStartChannel += width;
}
File.WriteAllBytes(filename, channelValues);
This just saves the data as byte array. If you want to write it as image, the simplest way is probably to make an 8-bit bitmap, open a BitmapData object on it, and write the lines into it one by one, and then set its colour palette to a generated range from 0,0,0 to 255,255,255.
I posted a function that takes a byte array, image dimensions and a palette and makes an image out of it in this answer.
How can I calculate Total of an Image; the overall total of all elements.
The source Image is ARGB format, Each color channel can be treated as single 2D matrix. Is this approach right?
Pseudo code:
Total[ImageData[img],2] (*Mathematica code*)
Below is the C# code I have tried; However, I dont understand how to convert the Byte value to Real in C#. Real is Double data type in C#.
// Calculate total of Image; Total[ImageData[img]];
Rectangle rect = new Rectangle(0, 0, MskImg3G.Width, MskImg3G.Height);
System.Drawing.Imaging.BitmapData bmpData =
MskImg3G.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, MskImg3G.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = Math.Abs(bmpData.Stride) * MskImg3G.Height;
byte[] TotalValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, TotalValues, 0, bytes);
// do something with the array
//Console.WriteLine(rgbValues.Length);
int sum = 0; foreach (byte b in TotalValues) sum += b;
Console.WriteLine(sum);//Total[ImageData[img, "Byte"], 2]
double vOut = Convert.ToDouble(sum);//Byte value - original ImageType - Real(MMA type)
Console.WriteLine(vOut);//Total[ImageData[img, "Byte"], 2]
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(TotalValues, 0, ptr, bytes);
MskImg3G.UnlockBits(bmpData);
//-------------------------TEST----------------------------------//
Both the console line outputs give: 2774762 is the Byte value, how can I get its Double(real) value?
> 2774762
> 2774762
How can achieve this in C# ?
Load the image :
Bitmap bitmap = Bitmap.FromFile("C:\\Temp\\img.bmp", true);
Get and process the pixels:
for for (int i=0;i<bitmap .Height;i++) for (int j=0;j<bitmap .width;j++)
{
Color c=bitmap .GetPixel(i,j) ;
// Do your totalisation(s) here using c.R, c.G and c.B
}
I have this image and I want to remove the background to isolate the green picture. The background is not completely black but it contains some pixels having the same color of other pixels inside the green picture.
I have used this
private void ButtonFilterClick(object sender, EventArgs e)
{
PixelFormat pxf = PixelFormat.Format24bppRgb;
Bitmap bitmap = ((Bitmap)(_smartLabForm.pictureBox1.Image));
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bmpData =
bitmap.LockBits(rect, ImageLockMode.ReadWrite, pxf);
IntPtr ptr = bmpData.Scan0;
int numBytes = bmpData.Stride * bitmap.Height;
byte[] rgbValues = new byte[numBytes];
Marshal.Copy(ptr, rgbValues, 0, numBytes);
for (int counter = 0; counter < rgbValues.Length; counter += 3)
{
if (rgbValues[counter] < 15 &&
rgbValues[counter + 1] < 15 &&
rgbValues[counter + 2] < 15)
{
rgbValues[counter] = 255;
rgbValues[counter + 1] = 255;
rgbValues[counter + 2] = 255;
}
}
Marshal.Copy(rgbValues, 0, ptr, numBytes);
bitmap.UnlockBits(bmpData);
_smartLabForm.Refresh();
}
and what I obtain is this:
How can I remove the "noise" remaining without damage the green picture and without affecting the performance?
Thank you?
This is actually a quite complex topic in computer vision (image segmentation). Covering the advanced techniques would be way too broad. But here is quick and simple idea that may get the job done:
Increase the threshold enough that all background pixels fall below it. When checking if a pixel should be removed, also compare all pixels in a certain neighborhood (e.g. circular radius) with the threshold. Only remove it if they are all below the threshold.
That way you remove pixels less agressively when you are near the feature region.
hope you all doing well. I did write a bit of codes in C# using Aforge library. I wanted to crop my main image captured from webcam so as to have a nice ROI. When I use threshold value of 0 everything should be in white pixels (total of lets say 26880 pixels) but it seems that I have some black pixels (578 pixels) within my cropped image. any idea of what may caused it? when I don't crop my image everything is fine.
Bitmap img = (Bitmap)eventArgs.Frame.Clone();
Bitmap bmp = new Bitmap(x2box, y2box);
bmp = img.Clone(new Rectangle(x1box, y1box, x2box, y2box), eventArgs.Frame.PixelFormat);
Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721);
Bitmap img1 = filter.Apply(bmp);
Threshold tresh = new Threshold((int)tresh1); // tresh1 is 0-255 but is set to zero here
tresh.ApplyInPlace(img1);
int iterator = 1; int xrow = 0; // here i use these constant to calculate location of the pixels
byte[] arraybyte = BitmapToByteArray(img1);
for (int i = 0; i < arraybyte.Length; i++)
{
if (i - iterator * img1.Width == 0)
{
xrow++;
iterator++;
}
if (arraybyte[i] == 0) // if pixel is black
{
X_val.Add(i - xrow * img1.Width);
Y_val.Add(iterator);
}
}
for (int i = 0; i < X_val.Count; i++)
{
YAve += Y_val[i];
XAve += X_val[i];
}
MessageBox.Show(X_val.Count.ToString()); // shows non-zero value!
the BitmapToByteArray method is as follow:
public static byte[] BitmapToByteArray(Bitmap bitmap)
{
BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
int numbytes = bmpdata.Stride * bitmap.Height;
byte[] bytedata = new byte[numbytes];
IntPtr ptr = bmpdata.Scan0;
Marshal.Copy(ptr, bytedata, 0, numbytes);
bitmap.UnlockBits(bmpdata);
return bytedata;
}
The number of bytes for each row of the Bitmap will be enforced to be a multiple of 4. If roi width * bytes per pixel is not a multiple of 4, you will have padding bytes at the end of each row.
They will not be thresholded as they aren't really part of the Bitmap, so their value may be 0. Your BitmapToByteArray method might not be padding-aware and read every byte.