- I have a coloured (or b&w) image as a Base64 string
- I have a target RGB
- I want to tint that image so that when it is resized down to a much smaller size, it's average RGB gets pretty close to my target RGB
You can see the below code in action here
const MIX_OPACITY = 93;
const SMALL_WIDTH = 6;
const targetRGB = {
r: 77,
g: 19,
b: 80
};
const targetHEX = rgbToHex(targetRGB.r, targetRGB.g, targetRGB.b);
(async () => {
let originalBase64 = fs.readFileSync(`./src/bob.jpeg`, {
encoding: "base64"
});
let jimpOriginal = await Jimp.read(new Buffer(originalBase64, "base64"));
let jimpGrey = jimpOriginal.greyscale();
let jimpTint = await new Promise((resolve) => {
jimpGrey.color(
[
// { apply: 'brighten', params: [50] }, // made things worse
{ apply: "mix", params: [targetHEX, MIX_OPACITY] } // 50 stands for opacity
],
(err, res) => {
resolve(res);
}
);
});
let tintedBase64 = await new Promise((resolve) => {
jimpTint.getBase64(Jimp.AUTO, (err, res) => {
resolve(res.replace(/^data:image\/(png|jpeg);base64,/, ""));
});
});
fs.writeFileSync(`${__dirname}/bob-tint.jpeg`, tintedBase64, "base64");
let resized = await resize(tintedBase64, SMALL_WIDTH, true);
fs.writeFileSync(
`${__dirname}/bob-tint-small.jpeg`,
resized.base64,
"base64"
);
let avgRGB = await avgRgb(resized.base64, { isBase64: true });
let deltaResult = delta(
[avgRGB.r, avgRGB.g, avgRGB.b],
[targetRGB.r, targetRGB.g, targetRGB.b]
);
console.log("deltaResult = ", deltaResult);
if (deltaResult <= 2) {
console.log("all is good");
}
})();
The Jimp library that I'm using is providing various color manipulation methods but right now I'm only trying out mix
. It also provides brighten
, lighten
,desaturate
, etc..
I'm trying to find the right combination of methods and values to reach the desired deltaResult <= 2
.
Is it possible and how ? Right now I'm stuck as I'm not really familiar with color manipulation.
I realised that increasing the opacity of the tint obviously reduces the deltaResult
to the desired value. However, increasing the opacity also reduces the clarity of the content as it is basically "darkening" the image to the desired color to the point where you mostly see the desired color and not the actual content of the photo (in my case a face). I think it's impossible to maintain both clarity of content while at the same time have and average RGB that is close to the desired color.
Increasing the MIX_OPACITY
in the code used in the link, reduces the deltaResult
but compromises clarity, and vice-versa.