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, orig_color, orig_color, is_upscaled=True) copy_color = sRGBColor(copy_color, copy_color, copy_color, 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.