|
Hi.
Your computation of number of bytes does not currently support all the pixel formats with all dimensions.
For example, the format Imaging.PixelFormat.Format32bppRgb your code computes 24576 bytes for the size of the byte array rather than 32768 as it should be.
I suggest instead computing the size of the byte array thus:
byteCount = bmpData.Stride * bmpData.Height
where the .Stride property is the number of bytes (regardless of format) per pixel row, including any padding at the end, which can happen with 16bpp and 24bpp formats with certain dimensions that do not have a row-byte-count evenly-divisible by 4. (Each row may have padding at the end so that each new row starts at an address that is evenly-divisible by 4.)
Then later you can support Indexed formats too if you want.
Also the SetPixel() and GetPixel() subroutines should also use bmpData.Stride instead of Width to compute the offset into the byte array correctly.
Kind regards,
Vic
modified 13-Apr-18 19:02pm.
|
|
|
|
|
Hi!
I suggest code like this:
Dim b As Integer = ibaPixelDataBytes(index)
Dim g As Integer = ibaPixelDataBytes(index + 1)
Dim r As Integer = ibaPixelDataBytes(index + 2)
Dim a As Integer = ibaPixelDataBytes(index + 3)
Return Color.FromArgb(a, r, g, b)
be converted to this for even more speed.
<pre>
Dim argb As Integer = System.BitConverter.ToInt32(ibaPixelDataBytes, index)
Return Color.FromArgb(argb)
-end-
|
|
|
|
|
Thank you very much!
Thanks also to the C# translator. I prefer C#.
Nico
|
|
|
|
|
Hello: I'm trying to use Fast Pixel and it seems to work for some of my bitmpas...while giving me this error for others:
Index was outside the boundws of the array(see ''it bombs on this line: ' to see exact line below). I understand that the array isn't big enough but i don't understand why it works for some of my bitmaps and not others.
When I save my bitmaps, I save them as 24 bit rgb color 300 dpi. I don't know much about resolutions and what format is best to save them as.
Any thoughts are truly appreciated.
Public Function GetPixel(ByVal x As Integer, ByVal y As Integer) As Color
If Not Me.locked Then
Throw New Exception("Bitmap not locked.")
Return Nothing
End If
If Me.IsAlphaBitmap Then
Dim index As Integer = ((y * Me.Width + x) * 4)
Dim b As Integer = Me.rgbValues(index)
Dim g As Integer = Me.rgbValues(index + 1)
Dim r As Integer = Me.rgbValues(index + 2)
Dim a As Integer = Me.rgbValues(index + 3)
Return Color.FromArgb(a, r, g, b)
Else
Dim index As Integer = ((y * Me.Width + x) * 3)
'it bombs on this line: Dim b As Integer = Me.rgbValues(index)
Dim g As Integer = Me.rgbValues(index + 1)
Dim r As Integer = Me.rgbValues(index + 2)
Return Color.FromArgb(r, g, b)
End If
End Function
|
|
|
|
|
I have a bitmap that is 641 X 480
Running the code as in the example yeilds different results when compared to just using the getpixel and setpixel on the bitmap object.
Here is the code to test and see the problem.
Dim fp As New FastPixel(bmp)
Dim x As Integer, y As Integer
Dim c As Color
fp.Lock()
For y = 0 To fp.Height - 1 Step 5
For x = 0 To fp.Width - 1
c = fp.GetPixel(x, y)
If c.GetBrightness < 0.3 Then
fp.SetPixel(x, y, Color.Red)
End If
Next
Next
fp.Unlock(True)
PictureBox1.Image = fp.Bitmap
And the getpixel and setpixel off the bitmap object as
Dim x As Integer, y As Integer
Dim c As Color
For y = 0 To bmp.Height - 1 Step 5
For x = 0 To bmp.Width - 1
c = bmp.GetPixel(x, y)
If c.GetBrightness < 0.3 Then
bmp.SetPixel(x, y, Color.Red)
End If
Next
Next
|
|
|
|
|
Your article demonstrated an improvement over SetPixel/GetPixel() baby. A good piece of work. I gave it 5. Thanks for sharing.
I am just curious about how we can make this work on MFC/GDI. What is the counterpart of scan0() in GDI?
-- modified at 18:34 Saturday 6th January, 2007
I've found the counterpart GetBitmapBits/GetDIBits(), which return the entire bitmap bits, instead of individual pixels like GetPixel(). Similarly, they work much faster than GetPixel() calls.
Best,
Jun
|
|
|
|
|
using System;
using System.Drawing;
using System.Drawing.Imaging;
class FastPixel {
private byte[] rgbValues;
private BitmapData bmpData;
private IntPtr bmpPtr;
private bool locked = false;
private bool _isAlpha = false;
private Bitmap _bitmap;
private int _width;
private int _height;
public int Width {
get {
return this._width;
}
}
public int Height {
get {
return this._height;
}
}
public bool IsAlphaBitmap {
get {
return this._isAlpha;
}
}
public Bitmap Bitmap {
get {
return this._bitmap;
}
}
public FastPixel(Bitmap bitmap) {
if (bitmap.PixelFormat == (bitmap.PixelFormat | PixelFormat.Indexed))
throw new Exception("Cannot lock an Indexed image.");
this._bitmap = bitmap;
this._isAlpha = (this.Bitmap.PixelFormat == (this.Bitmap.PixelFormat | PixelFormat.Alpha));
this._width = bitmap.Width;
this._height = bitmap.Height;
}
public void Lock() {
if (this.locked)
throw new Exception("Bitmap already locked.");
Rectangle rect = new Rectangle(0, 0, this.Width, this.Height);
this.bmpData = this.Bitmap.LockBits(rect, ImageLockMode.ReadWrite, this.Bitmap.PixelFormat);
this.bmpPtr = this.bmpData.Scan0;
if (this.IsAlphaBitmap) {
int bytes = (this.Width * this.Height) * 4;
this.rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(this.bmpPtr, rgbValues, 0, this.rgbValues.Length);
} else {
int bytes = (this.Width * this.Height) * 3;
this.rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(this.bmpPtr, rgbValues, 0, this.rgbValues.Length);
}
this.locked = true;
}
public void Unlock(bool setPixels) {
if (!this.locked)
throw new Exception("Bitmap not locked.");
// Copy the RGB values back to the bitmap;
if (setPixels)
System.Runtime.InteropServices.Marshal.Copy(this.rgbValues, 0, this.bmpPtr, this.rgbValues.Length);
// Unlock the bits.;
this.Bitmap.UnlockBits(bmpData);
this.locked = false;
}
public void Clear(Color colour) {
if (!this.locked)
throw new Exception("Bitmap not locked.");
if (this.IsAlphaBitmap) {
for (int index = 0; index < this.rgbValues.Length; index += 4) {
this.rgbValues[index] = colour.B;
this.rgbValues[index + 1] = colour.G;
this.rgbValues[index + 2] = colour.R;
this.rgbValues[index + 3] = colour.A;
}
} else {
for (int index = 0; index < this.rgbValues.Length; index += 3) {
this.rgbValues[index] = colour.B;
this.rgbValues[index + 1] = colour.G;
this.rgbValues[index + 2] = colour.R;
}
}
}
public void SetPixel(Point location, Color colour) {
this.SetPixel(location.X, location.Y, colour);
}
public void SetPixel(int x, int y, Color colour) {
if (!this.locked)
throw new Exception("Bitmap not locked.");
if (this.IsAlphaBitmap) {
int index = ((y * this.Width + x) * 4);
this.rgbValues[index] = colour.B;
this.rgbValues[index + 1] = colour.G;
this.rgbValues[index + 2] = colour.R;
this.rgbValues[index + 3] = colour.A;
} else {
int index = ((y * this.Width + x) * 3);
this.rgbValues[index] = colour.B;
this.rgbValues[index + 1] = colour.G;
this.rgbValues[index + 2] = colour.R;
}
}
public Color GetPixel(Point location) {
return this.GetPixel(location.X, location.Y);
}
public Color GetPixel(int x, int y) {
if (!this.locked)
throw new Exception("Bitmap not locked.");
if (this.IsAlphaBitmap) {
int index = ((y * this.Width + x) * 4);
int b = this.rgbValues[index];
int g = this.rgbValues[index + 1];
int r = this.rgbValues[index + 2];
int a = this.rgbValues[index + 3];
return Color.FromArgb(a, r, g, b);
} else {
int index = ((y * this.Width + x) * 3);
int b = this.rgbValues[index];
int g = this.rgbValues[index + 1];
int r = this.rgbValues[index + 2];
return Color.FromArgb(r, g, b);
}
}
}
|
|
|
|
|
I'm not sure I see the point of all this - for the speed differences to be manifest, surely you'd have to do a lot of pixel access, in which case writing your own code that doesn't add a function call or two for each pixel would be even faster ?
Christian Graus - Microsoft MVP - C++
Metal Musings - Rex and my new metal blog
|
|
|
|
|
Err, the point of this code is to create something faster than Bitmap.GetPixel/Bitmap.SetPixel.
I think it's far succeeded it's requirement.
startmenuex.com
|
|
|
|
|
By the way, thanks for the code translation!
startmenuex.com
|
|
|
|
|
I have a need to convert images to 1 bit and do this now by looking pixel by pixel and determining how dark the pixel is and i set it white or black based on that. this is painfully slow, about 5 seconds per decent sized image. I am wondering if this method would help? I know in C# you can use pointers and unsafe methods to do some stuff that is more efficient, but i'm using VB.
Thanks!
|
|
|
|
|
Yep,
I've done something very similar to that in VB.NET, it was over 6 months ago which means I have little memory of actual hows and whys.
As I recall it decides which pixels are 'whiteish' and colours them green and which pixels are 'blackish' and colours them red (or maybe vice versa), oh and it keeps count, but it should be pretty simple to modify to your purposes. No promises as to quality of the code, I am sure there are others who can do that for you, but it did the job.
Alex
PS. it strikes me that I almost certainly modified code written by someone else to do this and I wouldn't wish to steal their thunder. If anyone recognises who the original source might be please comment to credit them.
Public Class blackish_whiteish
Public Shared BlackPix As Integer
Public Shared WhitePix As Integer
Public Shared Function ProcessImage(ByRef b As Bitmap) As Bitmap
Return bwProcessImage(b)
End Function
Private Shared Function bwProcessImage(ByRef b As Bitmap) As Bitmap
'initialise
BlackPix = 0
WhitePix = 0
'create array to store pixel data
Dim resArray(b.Width) As Double
'lock bits
Dim bmData As BitmapData = b.LockBits(New Rectangle(0, 0, b.Width, b.Height), _
System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
'width of bitmap row in memory
Dim stride As Integer = bmData.Stride
'pointers to the start of the bitmap data
Dim scan0 As IntPtr = bmData.Scan0
' array to hold the colors
Dim pixels(stride * b.Height - 1) As Byte
Marshal.Copy(scan0, pixels, 0, pixels.Length)
'store position of current pixel
Dim position As Integer
' store the current pixels RGB:
Dim R0, G0, B0 As Integer
'sort out black, white, blackish, whiteish
For x As Integer = 0 To b.Width - 1
'zero the array
resArray(x) = 0
For y As Integer = 0 To b.Height - 1
'position of current pixel's first byte:
position = (y * stride) + (x * 4)
B0 = pixels(position)
G0 = pixels(position + 1)
R0 = pixels(position + 2)
If (B0 + G0 + R0 = 765) Or (B0 + G0 + R0 = 0) Then
'black or white
If (B0 + G0 + R0 = 0) Then
BlackPix = BlackPix + 1
resArray(x) = resArray(x) + 1
Else
WhitePix = WhitePix + 1
End If
Else
If (B0 + G0 + R0 > 382) Then
pixels(position) = Convert.ToByte(0)
pixels(position + 1) = Convert.ToByte(0)
pixels(position + 2) = Convert.ToByte(255)
WhitePix = WhitePix + 1
Else
pixels(position) = Convert.ToByte(0)
pixels(position + 1) = Convert.ToByte(255)
pixels(position + 2) = Convert.ToByte(0)
BlackPix = BlackPix + 1
resArray(x) = resArray(x) + 1
End If
End If
Next 'height
Next 'width
Marshal.Copy(pixels, 0, scan0, pixels.Length)
histoArray = resArray
b.UnlockBits(bmData)
Return b
End Function
-- modified at 6:08 Thursday 24th August, 2006
|
|
|
|
|
Having had a quick look around my hard drive, I think this is where I got the basics from:-
http://vb-helper.com/howto_net_lockbits_images_class.html
Alex
|
|
|
|
|
I will have to try this. This doesn't use fastpixel though, will it be quick for doing the conversion?
|
|
|
|
|
beckerben wrote: This doesn't use fastpixel though, will it be quick for doing the conversion?
True it doesn't use fastpixel, but it does use the same principle (lockbits).
This is much quicker than setpixel.
If you do want to try it, I suspect I should mention you will need the following at the top of the blackish_whiteish class
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
then in your project
NewBmp = blackish_whiteish.ProcessImage(SourceBmp)
I'm sure you could do just as well, if not better, using fastpixel.
In your original post you asked
beckerben wrote: I have a need to convert images to 1 bit and do this now by looking pixel by pixel and determining how dark the pixel is and i set it white or black based on that. this is painfully slow, about 5 seconds per decent sized image. I am wondering if this method would help?
I was simply answering your question saying 'yes it would help I used the same method (lockbits) myself to do something very similar' then I thought it would be helpful to copy the code to show how I did it. I now feel that perhaps, rather than helping, I was confusing the issue, so in answer to your original post....
Yes, it would help, I used the same method (lockbits) myself to do something very similar.
Alex
|
|
|
|
|
Copying whole bitmap using one Marshal.Copy operation is not 100% correct. Try to do this with 24 bpp bitmap with odd line width and you will see the problem. This can be done only when BitmapData.Stride = width * pixelSize. Otherwise, you need to copy every line using BitmapData.Stride property. You can see details here:
http://www.codeproject.com/cs/media/csharpgraphicfilters11.asp[^]
-- modified at 10:07 Wednesday 16th August, 2006
|
|
|
|
|
Whoa. Didn't notice that!
Any idea for a workaround?
What do you mean by copying from bitmapData.Stride?
|
|
|
|
|
I mean, if bitmap line width is not equal exactly to Stride, you need to copy every line separately. Beginning of the line must be calculated by formula: Scan0 + line_number * Stride.
This happens because of bitmap line alignment.
|
|
|
|
|
1. Allow file name to be specified as one of the contructors;
2. Allow method to return Image
3 Instead of Lock/SetPixel/Unlock include the Lock/Unlock in the body of SetPixel.
Other than that, good class maybe make a C# version.
We made the buttons on the screen look so good you'll want to lick them. Steve Jobs
|
|
|
|
|
norm .net wrote: 1. Allow file name to be specified as one of the contructors;
2. Allow method to return Image
3 Instead of Lock/SetPixel/Unlock include the Lock/Unlock in the body of SetPixel.
Other than that, good class maybe make a C# version.
Thanks
1. You could just use New FastPixel(New Bitmap("FILENAME"))
2. Bitmap returns a bitmap, you can convert this to an image if you like.
3. The purpose of using lock/setpixel/unlock is to speed up processing. You may have noticed that the pixel data only gets copied back to the bitmap when you unlock it.
|
|
|
|
|
You say there's not much to talk about in your article. How about describing how it sets the pixel in locked mode? Is your code in an unsafe block? I've not looked at your code, but I'm familiar with the technique. You have to use a pointer from Scan0 and come up with an offset for the pixel, then set the bytes depending on the pixel format. There's a lot of information there that would be interesting to talk about.
How about performing the lock in a using() statement? The using will lock and unlock the BitmapData so you can do as many SetPixels within the block as you want.
Logifusion[^]
If not entertaining, write your Congressman.
|
|
|
|
|
I know it wasn't my post, but...
Dustin Metzgar wrote: Is your code in an unsafe block?
Yep, this technique (seemingly well-documented at this point on newgroups, forums, etc.) does make use of "unsafe" or unmanaged code. I used the same general principle in my master's thesis project.
|
|
|
|
|
norm .net wrote: Instead of Lock/SetPixel/Unlock include the Lock/Unlock in the body of SetPixel
Sure about that one?
|
|
|
|
|
Think so
We made the buttons on the screen look so good you'll want to lick them. Steve Jobs
|
|
|
|
|