Floyd Steinberg Dithering 1 bit ...

Slava, Tue Mar 15 2016, 02:39AM

I'm trying to make a dithering program that converts RGB image into 1 bit dithered image...

Here is where I got the algorithm...

Link2

My C# code...

I can't get it to work for some reason, can someone help me?

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace FloydSteinberg001
{
	class MainClass
	{
		public static void SetColor(Bitmap bmp1, int x,int y, int grey)
		{
			if (grey < 256) {
				bmp1.SetPixel (x, y, Color.FromArgb (grey, grey, grey));
			}
		}

		public static int GetColor(Bitmap bmp1, int x, int y)
		{
			Color colin = bmp1.GetPixel (x, y);
			return (colin.R + colin.G + colin.B) / 3;
		}


		public static void Main (string[] args)
		{
			string inputfile = @"/home/slava/Desktop/house001.JPG";
			string outputfile = @"/home/slava/Desktop/house002.BMP";

			Bitmap bmpin = new Bitmap (inputfile);
			Bitmap bmpout = new Bitmap (bmpin.Width,bmpin.Height);


			for (int y=1; y<bmpin.Height-1; y++) {
				for (int x=1; x<bmpin.Width-1; x++) {
					int old_pixel = GetColor (bmpin, x, y);
					int new_pixel = old_pixel / 2;
					SetColor (bmpin, x, y, new_pixel);
					int quant_error = old_pixel - new_pixel;

					SetColor (bmpout, x+1, y, GetColor (bmpin, x+1, y) + (int)(quant_error * 0.43750));
					SetColor (bmpout, x-1, y+1, GetColor (bmpin, x-1, y+1) + (int)(quant_error * 0.1875));
					SetColor (bmpout, x, y+1, GetColor (bmpin, x, y+1) + (int)(quant_error * 0.3125));
					SetColor (bmpout, x+1, y+1, GetColor (bmpin, x+1, y+1) + (int)(quant_error * 0.0625));



				}
			}

			bmpout.Save (outputfile, ImageFormat.Bmp);

			Console.WriteLine ("DONE!");
			Console.ReadLine ();

		}
	}
}
Re: Floyd Steinberg Dithering 1 bit ...
Dr. Slack, Tue Mar 15 2016, 02:52AM

so let's see how well it did then, post the piccies!
Re: Floyd Steinberg Dithering 1 bit ...
Slava, Tue Mar 15 2016, 02:54AM

I can't get it to work for some reason....
Re: Floyd Steinberg Dithering 1 bit ...
Dr. Slack, Tue Mar 15 2016, 12:14PM

Ah, didn't spot that. OK, make a 8x8 greyscale bitmap, run your code on it, and post that and the output as tables, see if we can see anything obvious.
Re: Floyd Steinberg Dithering 1 bit ...
Bjørn, Tue Mar 15 2016, 02:23PM


I hacked it into working, your clipping in SetColor was not good and you did not diffuse the errors back into the bitmap. You should do this in a float array to avoid the rounding errors and speed it up.

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace FloydSteinberg001
{
    class MainClass
    {
        public static void SetColor(Bitmap bmp1, int x, int y, int grey)
        {
            grey = Math.Min(Math.Max(grey, 0), 255);
            bmp1.SetPixel(x, y, Color.FromArgb(grey, grey, grey));
        }

        public static int GetColor(Bitmap bmp1, int x, int y)
        {
            Color colin = bmp1.GetPixel(x, y);
            return (colin.R + colin.G + colin.B) / 3;
        }


        public static void Main(string[] args)
        {
            string inputfile = @"/home/slava/Desktop/house001.JPG";
            string outputfile = @"/home/slava/Desktop/house002.BMP";

            Bitmap bmpin = new Bitmap(inputfile);
            Bitmap bmpout = new Bitmap(bmpin.Width, bmpin.Height);


            for (int y = 1; y < bmpin.Height - 1; y++)
            {
                for (int x = 1; x < bmpin.Width - 1; x++)
                {
                    int old_pixel = GetColor(bmpin, x, y);

                    // Clip the pixel to 4 bit grayscale
                    int new_pixel = (int) (old_pixel & 0xf0) | (old_pixel >> 4);

                    SetColor(bmpin, x, y, (int) new_pixel);
                    int quant_error = old_pixel - new_pixel;

                    SetColor(bmpin, x + 1, y, GetColor(bmpin, x + 1, y) + (int)(quant_error * 0.43750));
                    SetColor(bmpin, x - 1, y + 1, GetColor(bmpin, x - 1, y + 1) + (int)(quant_error * 0.1875));
                    SetColor(bmpin, x, y + 1, GetColor(bmpin, x, y + 1) + (int)(quant_error * 0.3125));
                    SetColor(bmpin, x + 1, y + 1, GetColor(bmpin, x + 1, y + 1) + (int)(quant_error * 0.0625));
                }
            }

            bmpin.Save(outputfile, ImageFormat.Bmp);

            Console.WriteLine("DONE!");
            Console.ReadLine();

        }
    }
}
Re: Floyd Steinberg Dithering 1 bit ...
Slava, Tue Mar 15 2016, 03:16PM

I changed it to 2 bit, and my output is still grey scale...


using System; 
using System.Drawing; 
using System.Drawing.Imaging; 

namespace FloydSteinberg001 
{ 
	class MainClass 
	{ 
		public static void SetColor(Bitmap bmp1, int x, int y, int grey) 
		{ 
			grey = Math.Min(Math.Max(grey, 0), 255); 
			bmp1.SetPixel(x, y, Color.FromArgb(grey, grey, grey)); 
		} 

		public static int GetColor(Bitmap bmp1, int x, int y) 
		{ 
			Color colin = bmp1.GetPixel(x, y); 
			return (colin.R + colin.G + colin.B) / 3; 
		} 


		public static void Main(string[] args) 
		{ 
			string inputfile = @"/home/slava/Desktop/house001.JPG"; 
			string outputfile = @"/home/slava/Desktop/house002.BMP"; 

			Bitmap bmpin = new Bitmap(inputfile); 
			Bitmap bmpout = new Bitmap(bmpin.Width, bmpin.Height); 


			for (int y = 1; y < bmpin.Height - 1; y++) 
			{ 
				for (int x = 1; x < bmpin.Width - 1; x++) 
				{ 
					int old_pixel = GetColor(bmpin, x, y); 

					// Clip the pixel to 4 bit grayscale 
					int new_pixel = (int) (old_pixel & 0xf0) | (old_pixel >> 2); 

					SetColor(bmpin, x, y, (int) new_pixel); 
					int quant_error = old_pixel - new_pixel; 

					SetColor(bmpin, x + 1, y, GetColor(bmpin, x + 1, y) + (int)(quant_error * 0.43750)); 
					SetColor(bmpin, x - 1, y + 1, GetColor(bmpin, x - 1, y + 1) + (int)(quant_error * 0.1875)); 
					SetColor(bmpin, x, y + 1, GetColor(bmpin, x, y + 1) + (int)(quant_error * 0.3125)); 
					SetColor(bmpin, x + 1, y + 1, GetColor(bmpin, x + 1, y + 1) + (int)(quant_error * 0.0625)); 
				} 
			} 

			bmpin.Save(outputfile, ImageFormat.Bmp); 

			Console.WriteLine("DONE!"); 
			Console.ReadLine(); 

		} 
	} 
}
Re: Floyd Steinberg Dithering 1 bit ...
Slava, Tue Mar 15 2016, 03:18PM

I would like it to be 1 but black and white.
Re: Floyd Steinberg Dithering 1 bit ...
Bjørn, Tue Mar 15 2016, 10:16PM

You have to mask away all the least significant bits that you don't want and fill them with copies of the remaining most significant bits.
To be left with only 1 bit you have to mask away the 7 least significant bits and copy the most significant bits into the blank bits.

int new_pixel = (int)(old_pixel & 0x80);
                    new_pixel |= new_pixel >> 1;
                    new_pixel |= new_pixel >> 2;
                    new_pixel |= new_pixel >> 4;

In C# you can do that simpler by

int new_pixel = old_pixel > 127 ? 255 : 0;
Re: Floyd Steinberg Dithering 1 bit ...
Slava, Tue Mar 15 2016, 10:52PM

I changed the code but now it gets stuck in an infinite loop...


using System; 
using System.Drawing; 
using System.Drawing.Imaging; 

namespace FloydSteinberg001 
{ 
	class MainClass 
	{ 
		public static void SetColor(Bitmap bmp1, int x, int y, int grey) 
		{ 
			grey = Math.Min(Math.Max(grey, 0), 255); 
			bmp1.SetPixel(x, y, Color.FromArgb(grey, grey, grey)); 
		} 

		public static int GetColor(Bitmap bmp1, int x, int y) 
		{ 
			Color colin = bmp1.GetPixel(x, y); 
			return (colin.R + colin.G + colin.B) / 3; 
		} 


		public static void Main(string[] args) 
		{ 
			string inputfile = @"C:\Users\Rostislav Persion\Desktop\radiowave0002.jpg"; 
			string outputfile = @"L:\me002.BMP"; 

			Bitmap bmpin = new Bitmap(inputfile); 
			Bitmap bmpout = new Bitmap(bmpin.Width, bmpin.Height); 


			for (int y = 1; y < bmpin.Height - 1; y++) 
			{ 
				for (int x = 1; x < bmpin.Width - 1; x++) 
				{ 
					int old_pixel = GetColor(bmpin, x, y); 

					// Clip the pixel to 4 bit grayscale 
					//int new_pixel = (int) (old_pixel & 0xf0) | (old_pixel >> 2); 
					int new_pixel = old_pixel > 127 ? 255 : 0; 

					SetColor(bmpin, x, y, (int) new_pixel); 
					int quant_error = old_pixel - new_pixel; 

					SetColor(bmpin, x + 1, y, GetColor(bmpin, x + 1, y) + (int)(quant_error * 0.43750)); 
					SetColor(bmpin, x - 1, y + 1, GetColor(bmpin, x - 1, y + 1) + (int)(quant_error * 0.1875)); 
					SetColor(bmpin, x, y + 1, GetColor(bmpin, x, y + 1) + (int)(quant_error * 0.3125)); 
					SetColor(bmpin, x + 1, y + 1, GetColor(bmpin, x + 1, y + 1) + (int)(quant_error * 0.0625)); 
				} 
			} 

			bmpin.Save(outputfile, ImageFormat.Bmp); 

			Console.WriteLine("DONE!"); 
			Console.ReadLine(); 

		} 
	} 
}
Re: Floyd Steinberg Dithering 1 bit ...
Bjørn, Wed Mar 16 2016, 11:38AM

If I copy and paste the code it works fine, it takes about 10 seconds for a million pixels on my PC. So maybe you are not waiting long enough.
Re: Floyd Steinberg Dithering 1 bit ...
Slava, Wed Mar 16 2016, 09:47PM

D
Re: Floyd Steinberg Dithering 1 bit ...
Bjørn, Wed Mar 16 2016, 09:56PM

Your output picture is fine and only has two different colour values, black and white. With the exception of the top and bottom line because your loop indexes are not completely correct.

If you see something else then you are viewing the picture in a scaled form that has aliasing.