HomeImagickImagickDrawImagickPixelImagick Pixel IteratorImagick KernelDevelopmentTutorial Source codeReport an issue
Category
Example

Eye color resolution

← fxAnalyzeImage   deconstructGif →

Human vision is... 'interesting'.

The way that your brain perceives the photons that enter your eyeball is optimized to throw away 'useless' information and enhance 'useful' information. This results in human perception being vulnerable to multiple 'optical illusions'.

You can't tell if two colors are the same or different. You perceive movement where there is none. Your brain gets bored of seeing pink and decides that green is far more interesting. Aka your visual perception of the world is full of bugs.

This page allows you to experiment with how your eye perceives resolution by downsampling the separate color channels of images in different colorspaces. Some examples will probably be easier to understand than a technical explanation.

For RGB colorspace, if either the red or green channels are downsampled, you notice very quickly. If the blue channel is downsampled, then your eyes don't really notice, even if the image features blue quite prominently

For the YIC colorspace, a high sample rate for the Y channel, low sample rate for I and C channels, give a better image quality than the I channel or C channel having high sample rates.

The image below shows the reconstructed image in the top left, and representations of the three color channels for the other three parts.


Example

/**
 * Downsamples an image to be a lower resolution, while keeping the same canvas dimensions.
 * aka combines the pixels into 'blockier' pixels.
 *
 * @param Imagick $imagick The image to use
 * @param int $pixel_sample_rate dimensions of the pixel blocks.
 */
function downSampleImage(Imagick $imagick, int $pixel_sample_rate)
{
    $width = $imagick->getImageWidth();
    $height = $imagick->getImageHeight();

    // For each of the channels, downsample to reduce the image information
    // then resize back the the original image size.
    $imagick->resizeimage(
        $width / $pixel_sample_rate,
        $height / $pixel_sample_rate,
        Imagick::FILTER_LANCZOS,
        1.0
    );
    $imagick->resizeImage($width, $height, Imagick::FILTER_POINT, 1.0);
}

/**
 * Takes an image, converts to a new colorspace, separates the image
 * into into individual color channels, downsamples the
 * individual channels, then recombines the image to RGB color space.
 *
 * @param int $channel_1_sample
 * @param int $channel_2_sample
 * @param int $channel_3_sample
 * @param int $colorspace Which colorspace to do the downsampling in.
 * @param string $image_path Which image to use.
 */
function eyeColourResolution(
    int $channel_1_sample,
    int $channel_2_sample,
    int $channel_3_sample,
    int $colorspace,
    string $image_path
) {
    // Create the source image and get the dimension of it.
    $imagick = new \Imagick(realpath($image_path));

    // Make the image smaller to make easier to compare channels.
    $imagick->resizeimage(
        $imagick->getImageWidth() / 2,
        $imagick->getImageHeight() / 2,
        Imagick::FILTER_LANCZOS,
        1
    );

    $width = $imagick->getImageWidth();
    $height = $imagick->getImageHeight();

    // Transform the image to the color space that we're going to separate
    // the channel in.
    $imagick->transformImageColorspace($colorspace);

    // Separate the 3 channels to individual images.
    $channel1 = clone $imagick;
    $channel1->separateImageChannel(Imagick::CHANNEL_RED);
    $channel2 = clone $imagick;
    $channel2->separateImageChannel(Imagick::CHANNEL_GREEN);
    $channel3 = clone $imagick;
    $channel3->separateImageChannel(Imagick::CHANNEL_BLUE);

    // For technical reasons, the neutral color should be black for
    // some colour spaces where 0 = no signal, and gray for color spaces
    // where 0.5 = no signal
    if ($colorspace === Imagick::COLORSPACE_RGB) {
        $neutralColor = 'black';
    }
    else {
        $neutralColor = 'gray';
    }

    // Create an empty canvas that we will use to recombine the separate images.
    $canvas = new Imagick();
    $canvas->newPseudoImage(
        $imagick->getImageWidth() * 2,
        $imagick->getImageHeight() * 2,
        "canvas:" . $neutralColor
    );

    // Make the canvas image be the correct color space to copy the individual channels
    // back correctly.
    $canvas->transformImageColorspace($colorspace);

    // Downsample the individual channels
    downSampleImage($channel1, $channel_1_sample);
    downSampleImage($channel2, $channel_2_sample);
    downSampleImage($channel3, $channel_3_sample);

    // Copy the individual channels into the canvas.
    $canvas->compositeImage($channel1, Imagick::COMPOSITE_COPYRED, 0, 0);
    $canvas->compositeImage($channel1, Imagick::COMPOSITE_COPYRED, $width, 0);

    $canvas->compositeImage($channel2, Imagick::COMPOSITE_COPYGREEN, 0, 0);
    $canvas->compositeImage($channel2, Imagick::COMPOSITE_COPYGREEN, 0, $height);

    $canvas->compositeImage($channel3, Imagick::COMPOSITE_COPYBLUE, 0, 0);
    $canvas->compositeImage($channel3, Imagick::COMPOSITE_COPYBLUE, $width, $height);

    // Convert the final image back to RGB for display
    $canvas->transformImageColorspace(Imagick::COLORSPACE_SRGB);

    // make it bigger so it's easier to see.
    $canvas->resizeimage(
        $canvas->getImageWidth() * 2,
        $canvas->getImageHeight() * 2,
        Imagick::FILTER_POINT,
        1
    );

    // Output the image.
    $canvas->setImageFormat('jpg');
    header("Content-Type: image/jpeg");
    echo $canvas->getImageBlob();
}