Skip to content

Instantly share code, notes, and snippets.

@to
Created January 31, 2025 05:18
Show Gist options
  • Select an option

  • Save to/5565bcaadcea1b7e2dff191c2758d4fd to your computer and use it in GitHub Desktop.

Select an option

Save to/5565bcaadcea1b7e2dff191c2758d4fd to your computer and use it in GitHub Desktop.
const sharp = require('sharp');
const colorConvert = require('color-convert');
async function colorTransfer() {
// 画像を読み込む
const sourceImage = await sharp('./source.jpg').raw().toBuffer({ resolveWithObject: true });
const targetImage = await sharp('./target.jpg').raw().toBuffer({ resolveWithObject: true });
const { data: sourceData, info: sourceInfo } = sourceImage;
const { data: targetData, info: targetInfo } = targetImage;
if (sourceInfo.width !== targetInfo.width || sourceInfo.height !== targetInfo.height) {
throw new Error('Source and target images must have the same dimensions');
}
const width = sourceInfo.width;
const height = sourceInfo.height;
const dstData = Buffer.alloc(sourceData.length);
// RGBからLabへの変換
const sourceLab = [];
const targetLab = [];
for (let i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
const offset = (i * width + j) * 3;
const sourceR = sourceData[offset];
const sourceG = sourceData[offset + 1];
const sourceB = sourceData[offset + 2];
const targetR = targetData[offset];
const targetG = targetData[offset + 1];
const targetB = targetData[offset + 2];
sourceLab.push(colorConvert.rgb.lab([sourceR, sourceG, sourceB]));
targetLab.push(colorConvert.rgb.lab([targetR, targetG, targetB]));
}
}
// 平均値と標準偏差を計算
const mean = { source: [0, 0, 0], target: [0, 0, 0] };
const stddev = { source: [0, 0, 0], target: [0, 0, 0] };
for (let i = 0; i < sourceLab.length; i++) {
for (let c = 0; c < 3; c++) {
mean.source[c] += sourceLab[i][c];
mean.target[c] += targetLab[i][c];
}
}
for (let c = 0; c < 3; c++) {
mean.source[c] /= sourceLab.length;
mean.target[c] /= targetLab.length;
}
for (let i = 0; i < sourceLab.length; i++) {
for (let c = 0; c < 3; c++) {
stddev.source[c] += Math.pow(sourceLab[i][c] - mean.source[c], 2);
stddev.target[c] += Math.pow(targetLab[i][c] - mean.target[c], 2);
}
}
for (let c = 0; c < 3; c++) {
stddev.source[c] = Math.sqrt(stddev.source[c] / sourceLab.length);
stddev.target[c] = Math.sqrt(stddev.target[c] / targetLab.length);
}
console.log('Source Mean:', mean.source);
console.log('Source StdDev:', stddev.source);
console.log('Target Mean:', mean.target);
console.log('Target StdDev:', stddev.target);
// 色変換の計算
for (let i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
const offset = (i * width + j) * 3;
const sourceIndex = i * width + j;
const newLab = [
((sourceLab[sourceIndex][0] - mean.source[0]) * (stddev.target[0] / stddev.source[0])) + mean.target[0],
((sourceLab[sourceIndex][1] - mean.source[1]) * (stddev.target[1] / stddev.source[1])) + mean.target[1],
((sourceLab[sourceIndex][2] - mean.source[2]) * (stddev.target[2] / stddev.source[2])) + mean.target[2]
];
const newRgb = colorConvert.lab.rgb(newLab);
dstData[offset] = Math.min(255, Math.max(0, newRgb[0])); // R
dstData[offset + 1] = Math.min(255, Math.max(0, newRgb[1])); // G
dstData[offset + 2] = Math.min(255, Math.max(0, newRgb[2])); // B
}
}
// 結果を保存
await sharp(dstData, {
raw: {
width: width,
height: height,
channels: 3
}
}).toFile('./dst.png');
}
colorTransfer().catch(console.error);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment