1

While moving forward with my algorithm for Ordered Dithering I got a problem, mainly I don't really know what col[levels] might be.

Here is the pseudocode

enter image description here

k - number of color values per channel

n - size of the threshold Bayers Matrix

My code which works somehow fine for K = 2, but it doesn't return correct result image when K = 3, K = 4 and so on

UPDATED CODE

class OrderedDithering
{
    private float[,] bayerMatrix;

    private float[,] dither2x2Matrix =
        new float[,] { { 1, 3 },
                    { 4, 2 } };

    private float[,] dither3x3Matrix =
        new float[,] { { 3, 7, 4 },
                    { 6, 1, 9 },
                     { 2, 8, 5 } };

    public BitmapImage OrderedDitheringApply(BitmapImage FilteredImage, int valuesPerChannel, int thresholdSize)
    {
        Bitmap bitmap = ImageConverters.BitmapImage2Bitmap(FilteredImage);

        if (thresholdSize == 2)
        {
            bayerMatrix = new float[2, 2];
            for (int i = 0; i < 2; ++i)
                for (int j = 0; j < 2; ++j)
                    bayerMatrix[i,j] = dither2x2Matrix[i,j] / 5;
        }
        else
        {
            bayerMatrix = new float[3, 3];
            for (int i = 0; i < 3; ++i)
                for (int j = 0; j < 3; ++j)
                    bayerMatrix[i, j] = dither3x3Matrix[i, j] / 10;
        }

        for (int i = 0; i < bitmap.Width; ++i)
            for(int j = 0; j < bitmap.Height; ++j)
            {

                Color color = bitmap.GetPixel(i, j);
                double r = Scale(0, 255, 0, 1, color.R);
                double g = Scale(0, 255, 0, 1, color.G);
                double b = Scale(0, 255, 0, 1, color.B);

                int counter = 0;
                counter += Dither(valuesPerChannel, r, thresholdSize, i, j);
                counter += Dither(valuesPerChannel, g, thresholdSize, i, j);
                counter += Dither(valuesPerChannel, b, thresholdSize, i, j);

                if (counter == 0)
                    bitmap.SetPixel(i, j, Color.FromArgb(0,0,0));
                else
                    bitmap.SetPixel(i, j, Color.FromArgb(255/counter, 255/counter, 255/counter));
            }

        return ImageConverters.Bitmap2BitmapImage(bitmap);
    }

    public int Dither(int valuesPerChannel, double colorIntensity, int thresholdSize, int i, int j)
    {
        double tempValue = (double)(Math.Floor((double)((valuesPerChannel - 1) * colorIntensity)));
        double re = (valuesPerChannel - 1) * colorIntensity - tempValue;

        if (re >= bayerMatrix[i % thresholdSize, j % thresholdSize])
            return 1;
        else
            return 0;
    }

    public double Scale(double a0, double a1, double b0, double b1, double a)
    {
        return b0 + (b1 - b0) * ((a - a0) / (a1 - a0));
    }
}

1 Answers1

0

If you just need a dithering library that works with System.Drawing and supports ordered dithering feel free to use mine. It has an easily usable OrderedDitherer class.

But if you are just playing for the sake of curiosity, and would like to improve your algorithm, here are some comments:

  • You now always set the pixels black or white (btw, you can use just Color.Black instead of FromArgb(0, 0, 0), for example), so it cannot work for more colors
  • It is unclear from the provided code whether a target palette belongs to colorsNum but basically you need to quantize all dithered pixels to the nearest transformed color.
  • So instead of working by brightness you should apply the ordered matrix for each color channel of each pixels (between the 0..255 range), and pick a color from the target palette for the result. The 'nearest color' can have more interpretation but generally an euclidean search will do it.
  • Try to avoid Bitmap.SetPixel/GetPixel as they are terribly slow and SetPixel does not even work with bitmaps that have indexed palette PixelFormat.
  • Your code does not show the values of your matrix but the values should also be calibrated for the palette you use. The typical default values are good for a BW palette but they are probably too strong for a 256 color palette (the dithering "jumps over" more shades and introduces rather just noise than nice gradients). See this page for more info.

Feel free to explore the linked code base. The Dither extension method is the starting point for dithering a Bitmap in-place, and here are the OrderedDitherer constructor, the strength calibration for the min/max values of the matrix and a fairly fast nearest color search either by RGB channels or by brightness.

György Kőszeg
  • 17,093
  • 6
  • 37
  • 65
  • Thank you for you reply. I have changed the code a bit, but I am somehow unsure if it returns proper images now. About your comments: That's true, there was no way of getting more colors, I have changed it now into a new logic and now more shades of gray can be seen. ColorsNum variable is a value that comes from the input, namely number of color values per channel (the naming wasn't good). Is there really a need to use the nearest color searching? –  Jun 12 '20 at 21:24
  • You can avoid nearest color searching if you can use a fast direct mapping. It is easy for a [grayscale palette](https://github.com/koszeggy/KGySoft.Drawing/blob/master/KGySoft.Drawing/Drawing/Imaging/_Quantizers/PredefinedColorsQuantizer.cs#L793). The custom mapping function can be passed to the `Palette` class I linked in the answer. – György Kőszeg Jun 12 '20 at 21:32