**
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;
}
Related
I have the following code which takes an array of bytes which i generated and writes them out to this bitmap. If i set the pixel format to Format4bppIndexed, then i get a readable image repeating width wise 4 times, if i set it to Format1bppIndexed(which is the correct setting) then i get one big unreadable image.
The image was a decoded Jbig2 image , i know the bytes are correct i can't seem to figure out how to get it into a 1bpp readable format.
Does anyone have any advice on that matter
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format1bppIndexed);
//Create a BitmapData and Lock all pixels to be written
BitmapData bmpData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly, bitmap.PixelFormat);
//Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(newarray, 0, bmpData.Scan0, newarray.Length);
//Unlock the pixels
bitmap.UnlockBits(bmpData);
The following may work although, if I remember correctly, Stride sometimes has an effect and a simple block-copy won't suffice (line by line must be used instead).
Bitmap bitmap = new Bitmap(
width,
height,
System.Drawing.PixelFormat.Format16bppGrayScale
);
To handle the Stride you'd want:
BitmapData^ data = bitmap->LockBits(oSize,
ImageLockMode::ReadOnly, bitmap->PixelFormat);
try {
unsigned char *pData = (unsigned char *)data->Scan0.ToPointer();
for( int x = 0; x < bmpImage->Width; ++x )
{
for( int y = 0; y < bmpImage->Height; ++y )
{
// Note: Stride is data width of scan line rounded up
// to 4 byte boundary.
// Requires use of Stride, not (width * pixelWidth)
int ps = y*bmpImage->Width*(nBitsPerPixel / 8)
+ x * (nBitsPerPixel / 8);
int p = y * data->Stride + x * (nBitsPerPixel / 8);
Byte lo = newarray[ps + 1];
Byte hi = newarray[ps + 0];
pData[p + 1] = lo;
pData[p + 0] = hi;
}
}
} finally {
bmpImage->UnlockBits(data);
}
Note: This was written in C++/CLI. Let me know if you need C# equivalents for any of the operations here. (Also, I pulled it from a read from bitmap rather than a write to bitmap so it may yet be a bit rough, but should hopefully give you the idea...)
I figured this out Although i'm still not sure why it should matter.
Based on this stackoverflow posting How can I load the raw data of a 48bpp image into a Bitmap?
I used the WPF classes instead of the GDI and wrote the code like this
var bitmap = new WriteableBitmap(width, height, 96, 96, System.Windows.Media.PixelFormats.BlackWhite, null);
bitmap.WritePixels(new System.Windows.Int32Rect(0, 0, width, height), newarray, stride, 0);
MemoryStream stream3 = new MemoryStream();
var encoder = new TiffBitmapEncoder ();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(stream3);
This correctly creates the image.
If anyone has any insight into why this might be the case please comment below
The port which now mostly works(lots of cleanup code) was based on a java implementation of JPedal Big2 Decoder to .NET. If anyone knows anyone interested send them here
https://github.com/devteamexpress/JBig2Decoder.NET
I have small function which will recolor pixels in a Bitmap from a given color to a new given color.
The problems I have with the code are as follows:
1)
The function gives results which are remapping white pixels which should not be concidered since I have a threshold... (unless I have defined this calculation wrong)
2) When certain colors are given e.g. LimeGreen wierd results are seen in the image returned from the function (I beleive this is due to overflow of the byte type in the addition or subtraction case)
The base image I am using can be found here:
http://www.freeimagehosting.net/uploads/c8745a9de1.png
Results I have obtained can be found here:
freeimagehosting.net/uploads/fa48e5a0eb.png (Called with Color.Magenta as remapColor, Color.Red as newColor, Seems like white pixels are effected and the end of the gradient is not colored correctly)
freeimagehosting.net/uploads/8faec6a569.png (Called with Color.Magenta as remapColor, Color.Yellow as newColor, Seems like white pixels are effected and the end of the gradient is not colored correctly)
freeimagehosting.net/uploads/2efd4c04aa.png (Called with Color.Magenta as remapColor, Color.Blue as newColor, Seems like gradient not colored correctly)
freeimagehosting.net/uploads/defdf04e16.png (Called with Color.Magenta as remapColor, Color.Teal as newColor, Seems like white pixels are effected and none of the gradient is calculated correctly)
The function I have for this code is below: UPDATED per suggestions
public unsafe static Bitmap RecolorImage(Bitmap original, Color remapColor, Color newColor)
{
Bitmap result = new Bitmap(original.Width, original.Height);
//lock the original bitmap in memory
BitmapData originalData = original.LockBits(
new Rectangle(0, 0, original.Width, original.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
//lock the new bitmap in memory
BitmapData newData = result.LockBits(
new Rectangle(0, 0, original.Width, original.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
//set the number of bytes per pixel
int pixelSize = 4;
int rthreshold = 128;
int gthreshold = 128;
int bthreshold = 128;
for (int y = 0; y < original.Height; y++)
{
//get the data from the original image
byte* oRow = (byte*)originalData.Scan0 + (y * originalData.Stride);
//get the data from the new image
byte* nRow = (byte*)newData.Scan0 + (y * newData.Stride);
for (int x = 0; x < original.Width; x++)
{
//examine the rgb values
byte r = (byte)((oRow[x * pixelSize]));
byte g = (byte)((oRow[x * pixelSize + 1]));
byte b = (byte)((oRow[x * pixelSize + 2]));
byte a = (byte)((oRow[x * pixelSize + 3]));
if (a > 0 &&
Math.Abs(remapColor.R - r) <= rthreshold &&
Math.Abs(remapColor.B - b) <= bthreshold &&
Math.Abs(remapColor.G - g) <= gthreshold
)
{
if (newColor.R == 0)
{
r = 0;
}
else
{
if (newColor.R > remapColor.R)
r = (byte)(r - newColor.R);
else
r = (byte)(r + newColor.R);
}
if (newColor.G == 0)
{
g = 0;
}
else
{
if (newColor.G > remapColor.G)
g = (byte)(g - newColor.G);
else
g = (byte)(g + newColor.G);
}
if (newColor.B == 0)
{
b = 0;
}
else
{
if (newColor.B > remapColor.B)
b = (byte)(b - newColor.B);
else
b = (byte)(b + newColor.B);
}
}
//set the new image's pixel remaped pixel color
nRow[x * pixelSize] = b; //B
nRow[x * pixelSize + 1] = g; //G
nRow[x * pixelSize + 2] = r; //R
nRow[x * pixelSize + 3] = a; //A
}
}
original.UnlockBits(originalData);
result.UnlockBits(newData);
return result;
}
What gives....
Is what I am trying to do possible?
Is it reliable?
Is there just a bug in my code?
Is there a better way to achive this "re-mapable technique" on bitmaps using gradients?
Thank you for your time.
It looks like your threshold test is incorrect. Take the line:
remapColor.R - r <= rthreshold
If the current pixel is white, then r will be 255, and the test will always be true, no matter what remapColor.R and rthreshold are.
I think Math.Abs(remapColor.R - r) might work.
And you're likely correct about your byte values being out of bounds. Fixing the threshold test might stop that from happening. Otherwise, try putting some bounds checking in to see where it's happening.
I have decided that although this may be possible if I study the various materials regarding color spaces and their supporting theories. It seems that this will take a bit more than some quick threshold calculation and normalization to the remapColor.
I am going to propose that instead of performing this type of modification on a raster bitmap image that the graphics be modified in their vector form.
The process should be something like this:
The graphics are created in whatever imaging suite the designer is working in.
They are saved to a vector format e.g. SVG this will allow the customizable paths to be named, traversed and altered programmatically (and for more than color if needed) with SVG Rendering Engine(http://svg.codeplex.com/)
With this solution we can either output the SVG direct to the browser if supported and do the modifications directly on the client or use the server and output as PNG when needed.
I feel that this arrangement will provide us with more flexibility and a more robust solution than what I was initially going to hack together.
Thank you guys for your time!
Is there an efficient way of adjusting the contrast of an image in C#?
I've seen this article which advocates doing a per-pixel operation. Not quick.
I'm using colour matrices in places already and find them to be quick. Is there a way to adjust contrast using them? (Note: This guy gets it wrong.)
I'm also using EmguCV. I notice that OpenCV (which Emgu wraps) seems to have a contrast function - is there any way of accessing this through Emgu? At the moment all I can do in Emgu is normalise the histogram, which does change the contrast, but not with any degree of control on my part.
Anyone got any ideas?
If the code in that sample works for you, you can speed it up massively (by orders of magnitude) by using Bitmap.LockBits, which returns a BitmapData object that allows access to the Bitmap's pixel data via pointers. There are numerous samples on the web and on StackOverflow that show how to use LockBits.
Bitmap.SetPixel() and Bitmap.GetPixel() are the slowest methods known to mankind, and they both utilize the Color class, which is the slowest class known to mankind. They should have been named Bitmap.GetPixelAndByGodYoullBeSorryYouDid() and Bitmap.SetPixelWhileGettingCoffee as a warning to unwary developers.
Update: If you're going to modify the code in that sample, note that this chunk:
System.Drawing.Bitmap TempBitmap = Image;
System.Drawing.Bitmap NewBitmap = new System.Drawing.Bitmap(TempBitmap.Width,
TempBitmap.Height);
System.Drawing.Graphics NewGraphics =
System.Drawing.Graphics.FromImage(NewBitmap);
NewGraphics.DrawImage(TempBitmap, new System.Drawing.Rectangle(0, 0,
TempBitmap.Width, TempBitmap.Height),
new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height),
System.Drawing.GraphicsUnit.Pixel);
NewGraphics.Dispose();
can be replaced with this:
Bitmap NewBitmap = (Bitmap)Image.Clone();
Update 2: Here is the LockBits version of the AdjustContrast method (with a few other speed improvements):
public static Bitmap AdjustContrast(Bitmap Image, float Value)
{
Value = (100.0f + Value) / 100.0f;
Value *= Value;
Bitmap NewBitmap = (Bitmap)Image.Clone();
BitmapData data = NewBitmap.LockBits(
new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height),
ImageLockMode.ReadWrite,
NewBitmap.PixelFormat);
int Height = NewBitmap.Height;
int Width = NewBitmap.Width;
unsafe
{
for (int y = 0; y < Height; ++y)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
int columnOffset = 0;
for (int x = 0; x < Width; ++x)
{
byte B = row[columnOffset];
byte G = row[columnOffset + 1];
byte R = row[columnOffset + 2];
float Red = R / 255.0f;
float Green = G / 255.0f;
float Blue = B / 255.0f;
Red = (((Red - 0.5f) * Value) + 0.5f) * 255.0f;
Green = (((Green - 0.5f) * Value) + 0.5f) * 255.0f;
Blue = (((Blue - 0.5f) * Value) + 0.5f) * 255.0f;
int iR = (int)Red;
iR = iR > 255 ? 255 : iR;
iR = iR < 0 ? 0 : iR;
int iG = (int)Green;
iG = iG > 255 ? 255 : iG;
iG = iG < 0 ? 0 : iG;
int iB = (int)Blue;
iB = iB > 255 ? 255 : iB;
iB = iB < 0 ? 0 : iB;
row[columnOffset] = (byte)iB;
row[columnOffset + 1] = (byte)iG;
row[columnOffset + 2] = (byte)iR;
columnOffset += 4;
}
}
}
NewBitmap.UnlockBits(data);
return NewBitmap;
}
NOTE: this code requires using System.Drawing.Imaging; in your class' using statements, and it requires that the project's allow unsafe code option be checked (on the Build Properties tab for the project).
One of the reasons GetPixel and SetPixel are so slow for pixel-by-pixel operations is that the overhead of the method call itself starts to become a huge factor. Normally, my code sample here would be considered a candidate for refactoring, since you could write your own SetPixel and GetPixel methods that use an existing BitmapData object, but the processing time for the math inside the functions would be very small relative to the method overhead of each call. This is why I removed the Clamp calls in the original method as well.
One other way to speed this up would be to simply make it a "destructive" function, and modify the passed Bitmap parameter instead of making a copy and returning the modified copy.
#MusiGenesis,
Just wanted to note that I used this method for an image editor I've been writing. It works well, but sometimes this method triggers an AccessViolationException on this line:
byte B = row[columnOffset];
I realised it was because there was no standardisation of BitDepth, so if an image was 32 bit colour I was getting this error. So I changed this line:
BitmapData data = NewBitmap.LockBits(new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), ImageLockMode.ReadWrite, NewBitmap.PixelFormat);
to:
BitmapData data = NewBitmap.LockBits(new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
Hope this helps as it seems to have eradicated my problem.
Thanks for the post.
Jib
I'm a bit late, but use a color matrix implementation as these will be optimised for such transformations and is much easier than manipulating the pixels yourself: http://www.geekpedia.com/tutorial202_Using-the-ColorMatrix-in-Csharp.html
I got a problem with Lockbits. I'm searching black pixels, and because it does not seem right, I'm using a WindowPopup to print the colors of every pixel, but it's like my program is using another picture.
alt text http://i208.photobucket.com/albums/bb91/Savaronna/pixel-1.jpg?t=1234874238
I marked the first found black pixel red. As you can see there are several other pixels that should match too. What am I doing wrong?
This is my script, do I oversee something?
Bitmap b = this.TableListBMP;
BitmapData bmpData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format32bppPArgb);
int stride = bmpData.Stride;
IntPtr Scan0 = bmpData.Scan0;
unsafe
{
byte * p = (byte *)(void *)Scan0;
int nOffset = bmpData.Stride - b.Width*4;
int xOffset, yOffset;
for(int y=5; y<b.Height; ++y)
{
for(int x=1; x<b.Width; ++x)
{
MessageBox.Show(
string.Format("x={0}, y={1}, ARGB={2},{3},{4},{5}",
x, y,
Convert.ToString(p[(y*stride)+(x*4)]),
Convert.ToString(p[(y*stride)+(x*4)+1]),
Convert.ToString(p[(y*stride)+(x*4)+2]),
Convert.ToString(p[(y*stride)+(x*4)+3])));
p +=4;
}
// EDIT: This line should be removed
p += nOffset;
}
}
b.UnlockBits(bmpData);
If you are using offsets such as p[((y)*stride)+((x)*4)], you probably shouldn't be increasing p at all? Surely you've already handled that (x and y) with the multiplication? Also, bmpData.Stride - b.Width*4 is not (as far as I can see) a meaningful number... the stride is the rows size including padding.
Either remove the "p += ..." code, or change the offset algorithm. At the moment you are skipping data (bad) and accessing data outside of the object (very bad).
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.