Search code examples
image-processingimage-scaling

How to reverse (or scale down) dithering made by alternating two pixels?


I am looking for the formula (and its explanation) that gives the perceived color resulting from a quincunx arrangement of two other colors. The result is obviously not linear, because if I create an image with alternated RGB colors #E00000 (red) and #0000E0 (blue), and compare it with the mathematical average #700070, I see a big difference on the rendering.

On the image below, the left part is composed with #E00000 and #0000E0 colors, while the right is only #700070:

Comparison with #700070

I know there is a solution, because when I use GIMP to unzoom the dithered part, it magically continues to show the exact color I feel, but this time with an effective RGB code, which is #980098. This conversion is also made by the scaling feature when I select the "linear" algorithm... (Well, am I wrong, or despite its name it does not produces linearity at all?)

On the image below, the left part has still no purple in its pixels, whereas the right part is the #980098 purple:

Comparison with #980098

At least under my screen, if I don't look too carefully this image, it looks like a single purple band with no variation.

So what is the formula, and why? How to mathematically get this #980098 from #E00000 and #0000E0?


Solution

  • It looks like the following conversion procedure is applied:

    • Convert color levels from sRGB to Linear RGB color space (inverse gamma).
    • Compute the average color in Linear RGB color space.
    • Convert the average from Linear RGB to sRGB color space (apply gamma).

    Computing the average in Linear RGB color space gives better perceived output, compared to computing the average in sRGB color space.
    That is because the gamma correction (used with sRGB) makes sRGB color space non-linear.

    I thought that computing the average in LAB color space supposed to give better results, but at least for the given example, Linear RGB result looks better.


    MATLAB code sample (note that your input sample is #0000D0 and not #0000E0):

    % Create the dithering sample - used as reference
    I = zeros(100, 100, 3, 'uint8');
    I(1:2:end, 1:2:end, 1) = hex2dec('D0'); % Red
    I(2:2:end, 2:2:end, 1) = hex2dec('D0'); % Red
    I(1:2:end, 2:2:end, 3) = hex2dec('D0'); % Blue
    I(2:2:end, 1:2:end, 3) = hex2dec('D0'); % Blue
    figure;imshow(I);impixelinfo % Show I for testing
    
    
    % Convert the red and blue values from sRGB to linear RGB
    lin_red = rgb2lin(im2double(uint8([hex2dec('D0'), 0, 0])));  % #D00000
    lin_blue = rgb2lin(im2double(uint8([0, 0, hex2dec('D0')]))); % #0000D0
    
    % Compute the average in Linear RGB color space
    average_lin_lab = (lin_red + lin_blue) / 2;
    
    % Convert from Linear RGB to sRGB
    average_rgb = im2uint8(lin2rgb(average_lin_lab)); % [152 0 152] = #980098
    
    % Fill J matrix with the average color in sRGB color space
    J = zeros(100, 100, 3, 'uint8');
    J(:, :, 1) = average_rgb(1); % Red color channel
    J(:, :, 2) = average_rgb(2); % Green color channel
    J(:, :, 3) = average_rgb(3); % Blue color channel
    figure;imshow(J);impixelinfo % Show the result for testing
    

    Result (I and J images):
    enter image description hereenter image description here

    The RGB values of J are exactly #980098.