# Color matching of two images

This task seems to happen often, and the typical algorithm is
to match the histograms of the respective RGB channels.
For example @berak kindly provided an adaption of that

Still the results are not very satisfying sometimes.
I tried to use HSL instead, with not much improvement however.

The reason for the sometimes poor matching result seems
to be the separation of the color triples to the channels.
But building a “full 3D matching” would exhaust memory
(256^3 * 256^3 bit = 1TB for the resulting LUT,
while its generating will take even more for computing the mean).
Moreover this will take an eternity.

Coarsen the color to blocks of size 8 will result in degrading
quality and might still not be realistic.

So my question:
Is there a better idea to compute a “quality match operation”
between two versions of an image?
Time is not so important.

what’s the goal here ?

something like color-transfer ?

I thought that was obvious and a pretty common task… I’m sorry.

The task is to match two versions of an image, that differ because of the source
(eg. scan of positive and negative film, different scaners or cameras) to take best
of both versions (for example).

The wide spreaded alg for that (histogram matching of the individual channels;
the alg that you adapted (see cited post)) doesn’t produce good enough results.

Mathematical:
Given a relation [256]^3 to itself, consisting of say 30M entries.
The task is to compute a function of that.

That given I can image of

an alternative approach to compute the optimal matching LUT
(It prevents the memory consumption adding to the 1TB LUT,
but it probably will take even more time):
For every color (256^3):
Take the relation pairs with first element as that color.
Collect all corresponding right side’s entries.
Build a mean (in a “good way”) of them.
==> That will be the right side of that LUT entry.
Entries not hit that way have to be interpolated at last.

that’s why professional photo editing software uses lower resolution LUTs and interpolates.

calculating such a LUT is tricky. a photo pair rarely spans the entire cube. you’d have to fill in missing cells of the cube. you’d also have to pay special attention to the faces of the cube, where one color channel hits zero or full brightness.

1 Like

I’d like to pass some results I achieved so far.

Lab color space
As some of the color transform methods propagate Lab color space
for histogram matching… I tried that too.
To adapt after converting to Lab a scaling by 256 should be necessary
(for the following handling), and rescaling before retransforming.
Strangely it produces only garbage then, whereas omitting the scaling
produces a reasonable result.
Why’s all that? Something special about OpenCV’s conversions?

Quality of the transfers (“>” = “better than”)
Visual: RGB > HSL > Lab
Arithmetic: HSV > RGB > Lab

The method above doesn’t consider the inter-channel dependencies
as it treats the channels seperately.
There should be better results when using histograms of a channel
for every pair of the other channels (“3D histogram matching”):
Consider 256^2 histograms per channel instead of 1.
Actually I’m not sure how to change the existing alg
(generation of hist and CDF, transfer generation and applying LUT).

If the results of the above should still be not good enough
next could be to map not the frequency of the colors
but the (averaged) colors itself:

3D color LUT (3D cLUT)
I couldn’t find any alg./ impl. for generating a LUT
as transfer from one picture to the other.
I hesitate doing it by myself (again) as it’s not trivial
(see @crackwitz remarks) and I’ve the strong impression
this has been done by profs quite often since decades…

(NB: The LUT (frequency or color) will have to be subsampled
(==> cummulated entries),
due to resource for cLUTS and
due most entries will be 0 or 1 for frequency LUTs (as of
the ratio number of entries to number of pixels).

Still even that doesn’t accounts that the images’ difference may
be dependent of the pixels’ respective environment:
A color may be matched differently dependent on the environment it occurs in
I have no idea if that could be handled…