This is a continuation of my first attempt of optimizing a cheap HDMI grabber.

After my fiery failure with the first cheap USB grabber, I decided to give it another shot. This time I selected a more promising looking capture card with a metal casing. A friend of mine owns the same card, so I knew it uses the same chipset but another board design.

My goal was still to optimize the output colors. To put my research on more stable feet, I built myself a program that calculates the Delta E of colors in a custom color card that I created.

A custom script overlays the color card with the captured image to immediately show the color differences. The script also calculates the Delta E for each color as well as the average value. With the manufacturer settings, the color seems oversaturated, and the average Delta E is 5.6. This value is a pretty significant deviation.

import random, os, time

import cv2

from PIL import Image, ImageDraw

from colormath.color_objects import sRGBColor, LabColor
from colormath.color_conversions import convert_color
from colormath.color_diff import delta_e_cie2000

orig = Image.open("testcard.png")

cap = cv2.VideoCapture(3)
cap.set(cv2.CAP_PROP_FOURCC , cv2.VideoWriter_fourcc(*"MJPG") )
cap.set(3, 1920)
cap.set(4, 1080)
ret, captured = cap.read()
cap.release()
cv2.imwrite("captured.png", captured)
copy = Image.open("captured.png")

draw = ImageDraw.Draw(orig)

total_delta_e = 0
taken_measures = 0

for x in range(50, 1921-100, 200):
    for y in range(50, 1080-100, 200):
        copy_part = copy.crop(box=(x+75,y,x+151, y+151))
        orig.paste(copy_part, box=(x+75,y,x+151, y+151))

        orig_color = orig.getpixel((x+20, y+20))
        copy_color = orig.getpixel((x+20+75, y+20))

        orig_color = sRGBColor(orig_color[0], orig_color[1], orig_color[2], is_upscaled=True)
        copy_color = sRGBColor(copy_color[0], copy_color[1], copy_color[2], is_upscaled=True)

        orig_color_lab = convert_color(orig_color, LabColor)
        copy_color_lab = convert_color(copy_color, LabColor)

        delta_e = delta_e_cie2000(orig_color_lab, copy_color_lab)
        total_delta_e += delta_e
        taken_measures += 1

        draw.text((x+100, y+160), str(round(delta_e,2)), fill="white")

draw.text((50, 20), "Avg delta_e: " + str(round(total_delta_e/taken_measures, 2)), fill="white")

orig.save("overlay.png")

I used a script to iterate sensible value areas for brightness, contrast, saturation, and hue independently from each other. For each step, I calculated the average Delta E. The results are shown in the four diagrams below. The green line indicates the original Delta E of 5.6.

The script shows that even individual tweaks can already reduce the Delta E to about 4.4. With a bit of manual tweaking, I achieved a Delta E of 3.02 - close to the 3.0 that is the standard for displays used in image editing. Using another script, I searched for even better settings and finally achieved a Delta E of 2.88 using the parameters:

Brightness -9
Saturation 133
Contrast 137
Hue 0

The difference between manufacturer settings and the optimized values is visible in real capture scenarios. All in all, I am pretty happy with the results. Also, I enjoy that this capture card did not catch fire during my testing.