Skip to main content
2 of 6
deleted 5 characters in body
root
  • 111
  • 3

You can simulate what the subpixels look like, and optionally convert that to greyscale to see the intended color-artifact-free effect of the original subpixel rendering:

  • Create an image file that simulates what the screen looks like at 3x magnification, i.e. render each subpixel as three dedicated pixels of only that primary hue (red, green, or blue).
  • Optionally, convert to greyscale (and brighten 3x because the simulated image can't be bright without HDR).
  • Display the result at a high zoom level, otherwise the subpixels of the displaying device would interfere a tiny bit. Compensating for that as well is a different story.

Results for going from the top left to the bottom right image look nice:

originalgreyscale

↓                          ↑

simulated subpixelssimulated subpixels, greyscale

Python code:

import numpy as np
import matplotlib.pyplot as plt

def subpixel_render_and_save_all(image_path):
    # Load the image as an array and normalize to RGB values [0, 255]
    img_array = plt.imread(image_path)
    
    # Handle images with an alpha channel if present
    if img_array.shape[2] == 4:
        img_array = img_array[:, :, :3]
    img_array = (img_array * 255).astype(np.uint8)
    
    height, width, _ = img_array.shape

    # Create a base for the subpixel-rendered image (3x larger dimensions)
    subpixel_array = np.zeros((height * 3, width * 3, 3), dtype=np.uint8)

    # Place each color channel in its respective vertical subpixel column
    subpixel_array[:, 0::3, 0] = np.repeat(img_array[:, :, 0], 3, axis=0)  # Red
    subpixel_array[:, 1::3, 1] = np.repeat(img_array[:, :, 1], 3, axis=0)  # Green
    subpixel_array[:, 2::3, 2] = np.repeat(img_array[:, :, 2], 3, axis=0)  # Blue

    # Derive output file names
    base_filename = image_path.rsplit('.', 1)[0]
    colorful_output_path = f"{base_filename}_subpixel.png"
    grayscale_output_path = f"{base_filename}_subpixel_grayscale.png"
    downscaled_output_path = f"{base_filename}_subpixel_grayscale_downscaled.png"

    # Save the colorful subpixel-rendered image
    plt.imsave(colorful_output_path, subpixel_array)
    print(f"Colorful subpixel-rendered image saved to {colorful_output_path}")

    # Convert to grayscale using luminance method
    # grayscale_subpixel_array = (0.2989 * subpixel_array[:, :, 0] +
                                # 0.5870 * subpixel_array[:, :, 1] +
                                # 0.1140 * subpixel_array[:, :, 2]).astype(np.uint8)

    # Convert to grayscale
    grayscale_subpixel_array = (subpixel_array[:, :, 0] +
                                subpixel_array[:, :, 1] +
                                subpixel_array[:, :, 2]).astype(np.uint8)

    # Save the grayscale subpixel-rendered image
    plt.imsave(grayscale_output_path, grayscale_subpixel_array, cmap="gray")
    print(f"Grayscale subpixel-rendered image saved to {grayscale_output_path}")

    # Downscale by averaging each 3x3 block to a single pixel
    downscaled_array = grayscale_subpixel_array.reshape(height, 3, width, 3).mean(axis=(1, 3)).astype(np.uint8)

    # Save the downscaled grayscale image
    plt.imsave(downscaled_output_path, downscaled_array, cmap="gray")
    print(f"Downscaled grayscale image saved to {downscaled_output_path}")

# Example usage
subpixel_render_and_save_all("input_image.png")

This concrete method is similar to Simon F's mysterious comment.

root
  • 111
  • 3