How can reduce image brightness? [duplicate] - c#

This question already has an answer here:
Adjust brightness contrast and gamma of an image
(1 answer)
Closed 6 years ago.
I need to reduce the brightness of the picture using c#. After some analysis i have found one solution to reduce the brightness of the image by adjusting its each pixel color RGB values. Please find the codes from below:
Bitmap bmp = new Bitmap(Picture);
Reduce the picture color brightness
for (int i = 0; i < bmp.Width; i++)
{
for (int j = 0; j < bmp.Height; j++)
{
Color color = bmp.GetPixel(i, j);
color = ChangeColorBrightness(color, 0.80f);
bmp.SetPixel(i, j, color);
}
}
Method to reduce the RGB values of particular color:
private Color ChangeColorBrightness(Color color, float correctionFactor)
{
float red = (float)color.R;
float green = (float)color.G;
float blue = (float)color.B;
if (correctionFactor < 0)
{
correctionFactor = 1 + correctionFactor;
red *= correctionFactor;
green *= correctionFactor;
blue *= correctionFactor;
}
else
{
red = (255 - red) * correctionFactor + red;
green = (255 - green) * correctionFactor + green;
blue = (255 - blue) * correctionFactor + blue;
}
return Color.FromArgb(color.A, (int)red, (int)green, (int)blue);
}
This codes are working fine for my requirement but it will takes such amount of time when i am executing huge number of images with larger width and height.
Is there any other possibilities to achieve this requirement?

.GetPixel() and .SetPixel() are expensive - here is a faster appraoch
public static Bitmap ChangeColorBrightness(Bitmap bmp, float correctionFactor)
{
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;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
float correctionFactortemp = correctionFactor;
if (correctionFactor < 0)
{
correctionFactortemp = 1 + correctionFactor;
}
for (int counter = 1; counter < rgbValues.Length; counter ++)
{
float color = (float)rgbValues[counter];
if (correctionFactor < 0)
{
color *= (int)correctionFactortemp;
}
else
{
color = (255 - color) * correctionFactor + color;
}
rgbValues[counter] = (byte)color;
}
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
bmp.UnlockBits(bmpData);
return bmp;
}

Related

How to get color from specific area in an Image in C#

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;
}

How to apply blur effect on a bitmap image in C#? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
How can I apply blur effect on an image in C# without using a library?
Updated code (now much faster, requires use of UNSAFE keyword)
static void Main(string[] args)
{
Bitmap bitmap = new Bitmap("C:\\Users\\erik\\test.png");
bitmap = Blur(bitmap, 10);
bitmap.Save("C:\\Users\\erik\\test2.png");
}
private static Bitmap Blur(Bitmap image, Int32 blurSize)
{
return Blur(image, new Rectangle(0, 0, image.Width, image.Height), blurSize);
}
private unsafe static Bitmap Blur(Bitmap image, Rectangle rectangle, Int32 blurSize)
{
Bitmap blurred = new Bitmap(image.Width, image.Height);
// make an exact copy of the bitmap provided
using (Graphics graphics = Graphics.FromImage(blurred))
graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height),
new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
// Lock the bitmap's bits
BitmapData blurredData = blurred.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, blurred.PixelFormat);
// Get bits per pixel for current PixelFormat
int bitsPerPixel = Image.GetPixelFormatSize(blurred.PixelFormat);
// Get pointer to first line
byte* scan0 = (byte*)blurredData.Scan0.ToPointer();
// look at every pixel in the blur rectangle
for (int xx = rectangle.X; xx < rectangle.X + rectangle.Width; xx++)
{
for (int yy = rectangle.Y; yy < rectangle.Y + rectangle.Height; yy++)
{
int avgR = 0, avgG = 0, avgB = 0;
int blurPixelCount = 0;
// average the color of the red, green and blue for each pixel in the
// blur size while making sure you don't go outside the image bounds
for (int x = xx; (x < xx + blurSize && x < image.Width); x++)
{
for (int y = yy; (y < yy + blurSize && y < image.Height); y++)
{
// Get pointer to RGB
byte* data = scan0 + y * blurredData.Stride + x * bitsPerPixel / 8;
avgB += data[0]; // Blue
avgG += data[1]; // Green
avgR += data[2]; // Red
blurPixelCount++;
}
}
avgR = avgR / blurPixelCount;
avgG = avgG / blurPixelCount;
avgB = avgB / blurPixelCount;
// now that we know the average for the blur size, set each pixel to that color
for (int x = xx; x < xx + blurSize && x < image.Width && x < rectangle.Width; x++)
{
for (int y = yy; y < yy + blurSize && y < image.Height && y < rectangle.Height; y++)
{
// Get pointer to RGB
byte* data = scan0 + y * blurredData.Stride + x * bitsPerPixel / 8;
// Change values
data[0] = (byte)avgB;
data[1] = (byte)avgG;
data[2] = (byte)avgR;
}
}
}
}
// Unlock the bits
blurred.UnlockBits(blurredData);
return blurred;
}
Took 2.356 seconds to process 256x256 image with blur value 10.
Original Code (from Github - slightly altered)
static void Main(string[] args)
{
Bitmap bitmap = new Bitmap("C:\\Users\\erik\\test.png");
bitmap = Blur(bitmap, 10);
bitmap.Save("C:\\Users\\erik\\test2.png");
}
private static Bitmap Blur(Bitmap image, Int32 blurSize)
{
return Blur(image, new Rectangle(0, 0, image.Width, image.Height), blurSize);
}
private static Bitmap Blur(Bitmap image, Rectangle rectangle, Int32 blurSize)
{
Bitmap blurred = new Bitmap(image.Width, image.Height);
// make an exact copy of the bitmap provided
using (Graphics graphics = Graphics.FromImage(blurred))
graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height),
new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
// look at every pixel in the blur rectangle
for (int xx = rectangle.X; xx < rectangle.X + rectangle.Width; xx++)
{
for (int yy = rectangle.Y; yy < rectangle.Y + rectangle.Height; yy++)
{
int avgR = 0, avgG = 0, avgB = 0;
int blurPixelCount = 0;
// average the color of the red, green and blue for each pixel in the
// blur size while making sure you don't go outside the image bounds
for (int x = xx; (x < xx + blurSize && x < image.Width); x++)
{
for (int y = yy; (y < yy + blurSize && y < image.Height); y++)
{
Color pixel = blurred.GetPixel(x, y);
avgR += pixel.R;
avgG += pixel.G;
avgB += pixel.B;
blurPixelCount++;
}
}
avgR = avgR / blurPixelCount;
avgG = avgG / blurPixelCount;
avgB = avgB / blurPixelCount;
// now that we know the average for the blur size, set each pixel to that color
for (int x = xx; x < xx + blurSize && x < image.Width && x < rectangle.Width; x++)
for (int y = yy; y < yy + blurSize && y < image.Height && y < rectangle.Height; y++)
blurred.SetPixel(x, y, Color.FromArgb(avgR, avgG, avgB));
}
}
return blurred;
}
Took 7.594 seconds to process 256x256 image with blur value 10.
Orignal Image
Blurred Image (blur level 10)
Image from: https://www.pexels.com/search/landscape/
If you are using XAML check it from Microsoft. Also study this code it your problem can be solved.

Convert an image to grayscale parallel loop

I have written a code that converts image to grayscale. but the code only convert partial of it.
I am trying to convert this code to a Parallel computation. I end up with bugs that I can not get my head around them. Any suggestion?
private void button2_Click(object sender, EventArgs e)
{
Bitmap bmp = (Bitmap)pictureBox1.Image;
unsafe {
//get image dimension
//int width = bmp.Width;
//int height = bmp.Height;
BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
//define variable
int bpp = System.Drawing.Bitmap.GetPixelFormatSize(bmp.PixelFormat) / 8;
int hip = bitmapData.Height;
int wib = bitmapData.Width + bpp;
//point to first pixel
byte* PtrFirstPixel = (byte*)bitmapData.Scan0;
//color of pixel
// Color p;
//grayscale
Parallel.For(0, hip, y =>
{
byte* currentLine = PtrFirstPixel + (y * bitmapData.Stride);
for (int x = 0; x < wib; x = x + bpp)
{
//get pixel value
//p = bmp.GetPixel(x, y);
//extract pixel component ARGB
//int a = p.A;
//int r = p.R;
//int g = p.G;
// int b = p.B;
int b = currentLine[x];
int g = currentLine[x + 1];
int r = currentLine[x + 2];
//find average
int avg = (r + g + b) / 3;
//set new pixel value
// bmp.SetPixel(x, y, Color.FromArgb(a, avg, avg, avg));
currentLine[x] = (byte)avg;
currentLine[x + 1] = (byte)avg;
currentLine[x + 2] = (byte)avg;
}
});
bmp.UnlockBits(bitmapData);
//load grayscale image in picturebox2
//pictureBox2.Image = bmp;
}
pictureBox2.Image = bmp;
}
my out put image
int wib = bitmapData.Width + bpp;
should be:
int wib = bitmapData.Width * bpp;
You want the number of bytes which requires a multiply, not an add. There may be other issues, but this is definitely incorrect.

Make Image Slowly Visible on Form

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);
}
}
}

Use Webcam as ambient light sensor in PC

Is it possible to make the webcam of a pc acting as an ambient light sensor?
I am using .Net 4.5 framework in Windows 8 pro.
Philippe's answer to this question: How to calculate the average rgb color values of a bitmap has code that will calculate the average RGB values of a bitmat image (bm in his code):
BitmapData srcData = bm.LockBits(
new Rectangle(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
int stride = srcData.Stride;
IntPtr Scan0 = dstData.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];
}
}
}
}
int avgR = totals[0] / (width*height);
int avgG = totals[1] / (width*height);
int avgB = totals[2] / (width*height);
Once you have the average RGB values you can use Color.GetBrightness to determine how light or dark it is. GetBrightness will return a number between 0 and 1, 0 is black, 1 is white. You can use something like this:
Color imageColor = Color.FromARGB(avgR, avgG, avgB);
double brightness = imageColor.GetBrightness();
You could also convert the RGB values to HSL and look at the "L", that may be more accurate, I don't know.

Categories