I am trying to make an image black and white in c#. Here is my code:
public static void SetGrayscale(Bitmap b)
{
Bitmap temp = (Bitmap) b;
Bitmap bmap = (Bitmap)temp.Clone();
Color c;
for (int i = 0; i < bmap.Width; i++)
{
for (int j = 0; j < bmap.Height; j++)
{
c = bmap.GetPixel(i, j);
byte gray = (byte)(.299 * c.R + .587 * c.G + .114 * c.B);
bmap.SetPixel(i, j, Color.FromArgb(gray, gray, gray));
}
}
b = (Bitmap)bmap.Clone();
}
static void Main(string[] args)
{
Bitmap bm = new Bitmap(Image.FromFile("D:\\users\\visual studio 2010\\Projects\\aaa\\20130924_144411.tif"));
byte[] pixels = ImageToByte(bm);
SetGrayscale(bm);
}
The problem is, it does not turn into black and white, it is still the same. Am i not saving the changed image? What can be the problem here?
Thanks
Are you refering to that the file on disk doesn't change? You would have to save the grayscaled bitmap: bm.Save("D:\\bitmap.tif");
Related
I am trying to get color from specific area in an Image.
Assume that , this is image , and I want to get color inside image.(the result should be red of the above image) This color may be different position in image. Because I don't know exact position of color where it starting, so I can't get exact result.
Until now, I cropped image giving manually position of x and y, and then cropped image and I got average color of cropped image. But I know , this is not exact color.
What I tried :
private RgbDto GetRGBvalueCroppedImage(Image croppedImage)
{
var avgRgb = new RgbDto();
var bm = new Bitmap(croppedImage);
BitmapData srcData = bm.LockBits(
new Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
IntPtr Scan0 = srcData.Scan0;
long[] totals = new long[] { 0, 0, 0 };
int width = bm.Width;
int height = bm.Height;
unsafe
{
byte* p = (byte*)(void*)Scan0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
for (int color = 0; color < 3; color++)
{
int idx = (y * stride) + x * 4 + color;
totals[color] += p[idx];
}
}
}
}
avgRgb.avgB = (int)totals[0] / (width * height);
avgRgb.avgG = (int)totals[1] / (width * height);
avgRgb.avgR = (int)totals[2] / (width * height);
return avgRgb;
}
How can I get exact position to crop? May be I can convert image to byte array, then I can find different color and take position of it and then crop. But I have no clue how do this.
You can use something this extension method to get dominant color in a region of an image in case they are not all the same
public static Color GetDominantColor(this Bitmap bitmap, int startX, int startY, int width, int height) {
var maxWidth = bitmap.Width;
var maxHeight = bitmap.Height;
//TODO: validate the region being requested
//Used for tally
int r = 0;
int g = 0;
int b = 0;
int totalPixels = 0;
for (int x = startX; x < (startX + width); x++) {
for (int y = startY; y < (startY + height); y++) {
Color c = bitmap.GetPixel(x, y);
r += Convert.ToInt32(c.R);
g += Convert.ToInt32(c.G);
b += Convert.ToInt32(c.B);
totalPixels++;
}
}
r /= totalPixels;
g /= totalPixels;
b /= totalPixels;
Color color = Color.FromArgb(255, (byte)r, (byte)g, (byte)b);
return color;
}
You can then use it like
Color pixelColor = myBitmap.GetDominantColor(xPixel, yPixel, 5, 5);
there is room for improvement, like using a Point and Size, or even a Rectangle
public static Color GetDominantColor(this Bitmap bitmap, Rectangle area) {
return bitmap.GetDominantColor(area.X, area.Y, area.Width, area.Height);
}
and following this link:
https://www.c-sharpcorner.com/UploadFile/0f68f2/color-detecting-in-an-image-in-C-Sharp/
If you want to get the image colors, you don't need to do any cropping at all. Just loop on image pixels and find the two different colors. (Assuming that you already know the image will have exactly 2 colors, as you said in comments). I've written a small function that will do that. However, I didn't test it in an IDE, so expect some small mistakes:
private static Color[] GetColors(Image image)
{
var bmp = new Bitmap(image);
var colors = new Color[2];
colors[0] = bmp.GetPixel(0, 0);
for (int i = 0; i < bmp.Width; i++)
{
for (int j = 0; j < bmp.Height; j++)
{
Color c = bmp.GetPixel(i, j);
if (c == colors[0]) continue;
colors[1] = c;
return colors;
}
}
return colors;
}
I have some scanned image. I want to drop all color except black.
Problem is black color is not always rgb(0,0,0). So how can I do that?
Thanks.
You can use this code:
Bitmap myBitmap = new Bitmap(#"YourPath");
const float limit = 0.3f;
for (int i = 0; i < myBitmap.Width; i++)
{
for (int j = 0; j < myBitmap.Height; j++)
{
Color c = myBitmap.GetPixel(i, j);
if (c.GetBrightness() > limit)
{
myBitmap.SetPixel(i, j, Color.White);
}
}
}
myBitmap.Save(#"YourNewPath");
And play with limit to get what you want.
I'm writing a program that removes an overlay from a png image using the mask (the overlay image)
having image 1 and 2 I want to achive image 3.
I have tried using lockbits and tried many things but I can't do the math right I think
rgbValues is the byte array of overlay and rgbValues2 is the byte array of given image.
for (int counter = 0; counter < rgbValues.Length; counter ++)
{
int x = (counter / 4) * 4;
if (rgbValues[x + 3] != 0)
{
if (rgbValues[x + 3] == rgbValues2[x + 3])
{
rgbValues2[counter] = 0;
}
else
{
float a1 = (float)rgbValues[counter];
float a2 = (float)rgbValues2[counter] ;
float b1 = (float)rgbValues[x + 3];
float b2 = (float)rgbValues2[x + 3];
rgbValues2[counter] = (byte)(2 * a2- a1);
}
}
}
I've tried this with your sample Images, although they are composed in the same big image and looks like it works. The following code doesn't use LockBits for simplicity, it just gives you the idea, that is how to calculate the base color (in the third image) from the blend color (in the second image) and the result color (in the first image):
public Image ExtractBaseImage(Bitmap resultImage, Bitmap blendImage) {
Bitmap bm = new Bitmap(resultImage.Width, resultImage.Height);
for (int i = 0; i < resultImage.Width; i++) {
for (int j = 0; j < resultImage.Height; j++) {
Color resultColor = resultImage.GetPixel(i, j);
Color blendColor = blendImage.GetPixel(i, j);
if (blendColor.A == 0) bm.SetPixel(i, j, resultColor);
else if(blendColor != resultColor){
float opacity = blendColor.A / 255f;
int r = Math.Max(0,Math.Min(255,(int) ((resultColor.R - (opacity) * blendColor.R) / (1-opacity))));
int g = Math.Max(0,Math.Min(255,(int)((resultColor.G - (opacity) * blendColor.G) / (1-opacity))));
int b = Math.Max(0,Math.Min(255,(int)((resultColor.B - (opacity) * blendColor.B) / (1-opacity))));
bm.SetPixel(i,j,Color.FromArgb(r,g,b));
}
}
}
return bm;
}
Usage: Suppose the images are numbered as you did to the images posted in your question, we have image1, image2, image3 variables:
image3 = ExtractBaseImage((Bitmap)image1, (Bitmap)image2);
The idea is building a windows form application in Visual Studio 2010 using C#.
The program will run a series of operation when the user hit a button.
Is it possible to use a image to show the progress instead of using a progress bar?
So the idea is that the image will start of being invisible, and as the program progress, the image become more and more visible.
0% - invisible
50% - half transparent
100% - visible
I know you can toggle the PictureBox to be visible or not (PictureBox.Visible = true or false;), but is there a way to make it in between?
Any idea is appreciate.
Manipulating images in winforms is slow, so do this as little as possible:
public Bitmap ImageFade( Bitmap sourceBitmap, byte Transparency)
{
BitmapData sourceData = sourceBitmap.LockBits(new Rectangle(0, 0,
sourceBitmap.Width, sourceBitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];
Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);
sourceBitmap.UnlockBits(sourceData);
byte blue = 0;
byte green = 0;
byte red = 0;
byte a = 0;
int byteOffset = 0;
for (int offsetY = 0; offsetY <
sourceBitmap.Height; offsetY++)
{
for (int offsetX = 0; offsetX <
sourceBitmap.Width; offsetX++)
{
blue = 0;
green = 0;
red = 0;
a = 0;
byteOffset = offsetY *
sourceData.Stride +
offsetX * 4;
blue += pixelBuffer[byteOffset];
green += pixelBuffer[byteOffset + 1];
red += pixelBuffer[byteOffset + 2];
a += Transparency;//pixelBuffer[byteOffset + 3];
resultBuffer[byteOffset] = blue;
resultBuffer[byteOffset + 1] = green;
resultBuffer[byteOffset + 2] = red;
resultBuffer[byteOffset + 3] = a;
}
}
Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);
BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0,
resultBitmap.Width, resultBitmap.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
resultBitmap.UnlockBits(resultData);
return resultBitmap;
}
I note another answer uses SetPixel. Avoid using that function if possible. It's much faster to edit the underlying bytestream, which is still slow since it's not hardware accelerated, but it's the best of several not-great options
This function can potentially be further optimized, but I leave that as an exercise for the reader
You could always adjust the alpha component of your image:
void SetImageProgress(float percent, Bitmap img)
{
int alpha = (int)(percent / 100.0f * 255.0f);
alpha &= 0xff;
for(int x = 0; x < img.Width; x++)
{
for(int y = 0; y < img.Height; y++)
{
Color c = img.GetPixel(x, y);
c = Color.FromArgb(alpha, c.R, c.G, c.B);
img.SetPixel(x, y, c);
}
}
}
public Bitmap rotateImage()
{
try
{
curImgHndl.CurrentRotationHandler.Flip(RotateFlipType.Rotate90FlipNone);
}
catch (Exception ex)
{
}
return objCurrImageHandler.CurrentBitmap;
}
When an image is rotated several times (5 or more) using this function then it shows the error message
“Out Of Memory” .
To ratate an image in c#.net 4 I used ImageFunctions.dll. Decompiling the dll I have got the followings.
only a portion of the whole code is given that is used for rotation
public class RotationHandler
{
private ImageHandler imageHandler;
public void Flip(RotateFlipType rotateFlipType)
{
this.imageHandler.RestorePrevious();
Bitmap bitmap = (Bitmap) this.imageHandler.CurrentBitmap.Clone();
bitmap.RotateFlip(rotateFlipType);
this.imageHandler.CurrentBitmap = (Bitmap) bitmap.Clone();
}
}
How can I solve it?
the following method solve the problem as lazyberezovsky suggested.
public void Flip(RotateFlipType rotateFlipType)
{
this.imageHandler.RestorePrevious();
this.imageHandler.CurrentBitmap.RotateFlip(rotateFlipType);
}
But another problem in brightness method.
public void SetBrightness(int brightness)
{
Bitmap temp = (Bitmap)_currentBitmap;
Bitmap bmap = (Bitmap)temp.Clone();
if (brightness < -255) brightness = -255;
if (brightness > 255) brightness = 255;
Color c;
for (int i = 0; i < bmap.Width; i++)
{
for (int j = 0; j < bmap.Height; j++)
{
c = bmap.GetPixel(i, j);
int cR = c.R + brightness;
int cG = c.G + brightness;
int cB = c.B + brightness;
if (cR < 0) cR = 1;
if (cR > 255) cR = 255;
if (cG < 0) cG = 1;
if (cG > 255) cG = 255;
if (cB < 0) cB = 1;
if (cB > 255) cB = 255;
bmap.SetPixel(i, j, Color.FromArgb((byte)cR, (byte)cG, (byte)cB));
}
}
_currentBitmap = (Bitmap)bmap.Clone();
}
This method works for some images and doesn't work for other images and shows the following error
"SetPixel is not supported for images with indexed pixel formats."
It would be very much helpful if you could provide efficient and workable methods for rotation, crop and brightness.
please help again.
As Claudio mentions, you need to make sure you are disposing of any unused Bitmaps.
public void Flip(RotateFlipType rotateFlipType)
{
this.imageHandler.RestorePrevious();
Bitmap bitmap = (Bitmap) this.imageHandler.CurrentBitmap.Clone();
this.imageHandler.CurrentBitmap.Dispose(); // dispose of current bitmap
bitmap.RotateFlip(rotateFlipType);
Bitmap clone_map = (Bitmap) bitmap.Clone();
bitmap.Dipose(); // dispose of original cloned Bitmap
this.imageHandler.CurrentBitmap = clone_map;
}
You could probably simplify this to just:
public void Flip(RotateFlipType rotateFlipType)
{
this.imageHandler.RestorePrevious();
Bitmap bitmap = (Bitmap) this.imageHandler.CurrentBitmap.Clone();
this.imageHandler.CurrentBitmap.Dispose(); // dispose of current bitmap
bitmap.RotateFlip(rotateFlipType);
this.imageHandler.CurrentBitmap = bitmap;
}
Why not to rotate current bitmap, instead of creating copies?
public class RotationHandler
{
private ImageHandler imageHandler;
public void Flip(RotateFlipType rotateFlipType)
{
this.imageHandler.RestorePrevious();
this.imageHandler.CurrentBitmap.RotateFlip(rotateFlipType);
}
}