Remove gray from the photo c# windows forms

I want to remove grayness from the picture in pictureBox1, as in the first photo below. The second photo shows the formulas that can be used to do this. I wrote an approximate code, but the compiler throws an error, that variables red1, green1 and blue1 cannot be written to Color newPixel due to type incompatibility. Please help me fixing my code.

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Forms; 
 
namespace WindowsFormsApp2 
{ 
    public partial class Form1 : Form 
    { 
        public Form1() 
        { 
            InitializeComponent(); 
        } 
 
        private void pictureBox1_Click(object sender, EventArgs e) 
        { 
            OpenFileDialog dlg = new OpenFileDialog(); 
            dlg.Title = "Відкрити зображення"; 
            dlg.Filter = "jpg files (*.jpg)|*.jpg|All filles (*.*)|*.*"; 
            if (dlg.ShowDialog() == DialogResult.OK) 
            { 
                pictureBox1.Image = new Bitmap(dlg.OpenFile()); 
                pictureBox1.Height = pictureBox1.Image.Height; 
                pictureBox1.Width = pictureBox1.Image.Width; 
           
            } 
            dlg.Dispose(); 
            label1.Visible = false; 
        } 
 
        private void button1_Click(object sender, EventArgs e) 
        { 
            Bitmap input = new Bitmap(pictureBox1.Image); 
            Bitmap output = new Bitmap(input.Width, input.Height); 
            int[] red = new int[256]; 
            int[] green = new int[256]; 
            int[] blue = new int[256]; 
            for (int x = 0; x < input.Width; x++) 
            { 
                for (int y = 0; y < input.Height; y++) 
                { 
                    Color pixel = ((Bitmap)pictureBox1.Image).GetPixel(x, y); 
                    red[pixel.R]++; 
                    green[pixel.G]++; 
                    blue[pixel.B]++; 
 
                    double Ri = red.Average(); 
                    double Gi = green.Average(); 
                    double Bi = blue.Average(); 
                    double Avg = (Ri+Bi+Gi)/3; 
                    double red1 = red (Avg/Ri); 
                    double green1 = green(Avg / Gi); 
                    double blue1 = blue(Avg / Bi); 
                    Color newPixel = ((Color)red1| (Color)green1 | (Color)blue1); 
                    output.SetPixel(x, y, Color.((int)newPixel)); 
                } 
            } 
            pictureBox2.Image = output; 
        } 
    } 
}

photo1

photo2

Upvotes: 1

Views: 144

Answers (2)

You misinterpreted the formula. N in the formula specifies the total number of the pixels in the image. The overlined letters means the average channel value over the image. These variable should be the type of double. I called them redAverage, greenAverage and blueAverage, respectively. And as you already know, Avg is the average of these variables. Finally, R', G', B' are the new channel values calculated using the old values.

using System.Drawing.Imaging;

namespace Convert2Gray
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }
        private Bitmap m_inputImage;
        private Bitmap m_outputImage;

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();

            m_inputImage = (Bitmap)Image.FromFile("sample.png");
            m_outputImage = new Bitmap(m_inputImage.Width, m_inputImage.Height, m_inputImage.PixelFormat);
            pictureBox1.Image = m_inputImage;
            pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;

            DoConversion();

            pictureBox2.Image = m_outputImage;
            pictureBox2.SizeMode = PictureBoxSizeMode.AutoSize;

        }
        private unsafe void DoConversion()
        {
            BitmapData inputBitmapData = m_inputImage.LockBits(new Rectangle(0, 0, m_inputImage.Width, m_inputImage.Height), ImageLockMode.ReadWrite, m_inputImage.PixelFormat);
            BitmapData outputBitmapData = m_outputImage.LockBits(new Rectangle(0, 0, m_outputImage.Width, m_outputImage.Height), ImageLockMode.ReadWrite, m_outputImage.PixelFormat);

            byte* inputScan0 = (byte*)inputBitmapData.Scan0;
            byte* outputScan0 = (byte*)outputBitmapData.Scan0;

            int inputStride = inputBitmapData.Stride;
            int outputStride = outputBitmapData.Stride;

            int bytesPerPixel = Image.GetPixelFormatSize(m_inputImage.PixelFormat) / 8;

            double redAverage = 0.0;
            double greenAverage = 0.0;
            double blueAverage = 0.0;
            double average = 0.0; 
            int pixelCount = m_inputImage.Width * m_inputImage.Height;

             
            for (int y = 0; y < m_inputImage.Height; y++)
            {
                byte* inputCurrentRow = inputScan0 + y * inputStride;
                byte* outputCurrentRow = outputScan0 + y * outputStride;
                for (int x = 0; x < m_inputImage.Width; x++)
                { 
                    ColorBgr* inputColor = (ColorBgr*)(inputCurrentRow + x * bytesPerPixel); 

                    redAverage += inputColor->R;
                    greenAverage += inputColor->G;
                    blueAverage += inputColor->B; 
                }
            }
            redAverage /= pixelCount;
            greenAverage /= pixelCount;
            blueAverage /= pixelCount;
            average = (redAverage + greenAverage + blueAverage) / 3;

            for (int y = 0; y < m_inputImage.Height; y++)
            {
                byte* inputCurrentRow = inputScan0 + y * inputStride;
                byte* outputCurrentRow = outputScan0 + y * outputStride;
                for (int x = 0; x < m_inputImage.Width; x++)
                {
                    ColorBgr* inputColor = (ColorBgr*)(inputCurrentRow + x * bytesPerPixel);
                    ColorBgr* outputColor = (ColorBgr*)(outputCurrentRow + x * bytesPerPixel);

                    outputColor->R = (byte)(inputColor->R * average / redAverage);
                    outputColor->G = (byte)(inputColor->G * average / greenAverage);
                    outputColor->B = (byte)(inputColor->B * average / blueAverage);
                }
            } 
            m_inputImage.UnlockBits(inputBitmapData);
            m_outputImage.UnlockBits(outputBitmapData);
        }
        private struct ColorBgr : IEquatable<ColorBgr>
        {
            public byte B;
            public byte G;
            public byte R;
            public ColorBgr(byte b, byte g, byte r)
            {
                B = b;
                G = g;
                R = r;
            } 
            public static bool operator ==(ColorBgr left, ColorBgr right)
            {
                return left.Equals(right);
            }
            public static bool operator !=(ColorBgr left, ColorBgr right)
            {
                return !(left == right);
            }
            public bool Equals(ColorBgr other)
            {
                return this.B == other.B && this.G == other.G && this.R == other.R;
            } 
            public override bool Equals(object? obj)
            {
                if (obj is ColorBgr)
                    return Equals((ColorBgr)obj);
                return false;
            } 
            public override int GetHashCode()
            {
                return new byte[] { B, G, R }.GetHashCode();
            }
        } 
    }
}

The output of the example

Upvotes: 1

Xerillio
Xerillio

Reputation: 5261

You should use Color.FromArgb() to create the new color. However, this method expects 3 int as input and not double, so you need to convert your doubles to integers.

Simple type cast - (int)someDouble

This solution will simply remove any decimals from the double value (1.9 => 1):

double red1 = 123.45; 
double green1 = 12.345; 
double blue1 = 234.56; 
Color newPixel = Color.FromArgb((int)red1, (int)green1, (int)blue1);
// R=123, G=12, B=234

Math.Round(someDouble)

This solution will round the double value to the nearest integer (1.5 => 2 and 1.49 => 1):

int red1 = (int)Math.Round(123.45); 
int green1 = (int)Math.Round(12.345); 
int blue1 = (int)Math.Round(234.56); 
Color newPixel = Color.FromArgb(red1, green1, blue1);
// R=123, G=12, B=235

Upvotes: 2

Related Questions