Skip to content

Instantly share code, notes, and snippets.

@joshhunt
Created August 25, 2016 13:46
Show Gist options
  • Save joshhunt/cc7eff65e80d66236301e0d58cf0588a to your computer and use it in GitHub Desktop.
Save joshhunt/cc7eff65e80d66236301e0d58cf0588a to your computer and use it in GitHub Desktop.
var getPixels = require('get-pixels');
var _ = require('lodash');
process.stdout.write('\x1bc');
const BLACK_OR_WHITE_THRESHOLD = 0.09;
const DISTINCT_THRESHOLD = 0.25;
// Target:
// - bgColor 8,162,231
// - primaryColor 50,25,123
// - secondaryColor 8,8,17
// - detailColor 5,95,138
// Result:
// - bgColor 7, 162, 230
// - primaryColor 49, 26, 121
// - secondaryColor 7, 10, 17
// - detailColor 78, 41, 192
const imageUrl = 'https://imageresizer.static9.net.au/wwvpK-H2xY7NXXmwg3VBLpGiAz8=/768x0/http%3A%2F%2Fprod.static9.net.au%2F_%2Fmedia%2F9vod%2Fshows%2Fr%2Freal-housewives-of-bh%2Fkey-art%2F160805_rhbh_carousel.jpg';
getPixels(imageUrl, function(err, pixels) {
if(err) {
console.log('Error!')
console.log(err);
return
}
const { bgColor } = findEdgeColor(pixels);
const {
primaryColor,
secondaryColor,
detailColor,
} = getTextColors(pixels, bgColor);
logColors({bgColor, primaryColor, secondaryColor, detailColor})
})
function logColors(obj) {
for (name in obj) {
const color = obj[name];
console.log(`${name} ${color.red}, ${color.green}, ${color.blue}`);
}
}
// findEdgeColor - finds background color from edge of image
//
// get the most commonly occuring color along the left
// edge of the image that isnt blackish or whiteish
//
// TODO: if we don't find a color in the first row, continue searching
// in other rows
// TODO: get alt bg color
function findEdgeColor(pixels) {
const width = pixels.shape[0];
const height = pixels.shape[1];
const colorCount = {};
for (let y = 0; y < height; y++) {
const { red, green, blue } = getRgb(pixels, 0, y);
const key = [red, green, blue].join(',');
if (!colorCount[key]) {
colorCount[key] = {
count: 0,
color: { red, green, blue }
}
}
colorCount[key].count++
}
const sortedColors = sortColorCounts(colorCount);
const color = _.find(sortedColors, ({ color }) => {
return !isBlackOrWhite(color);
});
return {
bgColor: color.color,
}
};
// findTextColors - get three colors for the text
//
// Get the three most commonly occuring colors in the image
// that are:
// - sufficienly different enough from each other
// - sufficiently constrasted from the bg
// - light if the bg is dark, or dark if the bg is light
function getTextColors(pixels, bgColor) {
const width = pixels.shape[0];
const height = pixels.shape[1];
const colorCount = {};
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
const { red, green, blue } = getRgb(pixels, x, y);
const key = [red, green, blue].join(',');
if (!colorCount[key]) {
colorCount[key] = {
count: 0,
color: { red, green, blue }
}
}
colorCount[key].count++
}
}
const sortedColors = sortColorCounts(colorCount);
// TODO: find a more elegant way of writing this
const primaryColor = _.find(sortedColors, ({ color }) => {
return isContrastingColor(color, bgColor);
}).color;
const secondaryColor = _.find(sortedColors, ({ color }) => {
return isDistinct(primaryColor, color) && isContrastingColor(color, bgColor);
}).color;
const detailColor = _.find(sortedColors, ({ color }) => {
return isDistinct(primaryColor, color) && isDistinct(secondaryColor, color) && isContrastingColor(color, bgColor);
}).color;
return {
primaryColor,
secondaryColor,
detailColor,
}
}
function isWhite(channel) {
const threshold = 255 * (1 - BLACK_OR_WHITE_THRESHOLD);
return channel > threshold;
}
function isBlack(channel) {
const threshold = 255 * BLACK_OR_WHITE_THRESHOLD;
return channel < threshold;
}
function isBlackOrWhite(color) {
const colors = _.values(color);
const threshold = 0.09;
const thresholdWhite = 255 * (1 - threshold);
const thresholdBlack = 255 * threshold;
if (_.every(colors, isWhite) || _.every(colors, isBlack)) {
return true
}
return false
}
function getColorAsFraction(color) {
return {
red: color.red / 255,
green: color.green / 255,
blue: color.blue / 255,
}
}
function getLumance(color) {
const { red, green, blue } = getColorAsFraction(color)
// lol magic numbers
return (0.2126 * red) + (0.7152 * green) + (0.0722 * blue);
}
function isContrastingColor(colorA, colorB) {
const lumA = getLumance(colorA);
const lumB = getLumance(colorB);
const contrast = lumA > lumB
? (lumA + 0.05) / (lumB + 0.05)
: (lumB + 0.05) / (lumA + 0.05);
return contrast > 1.6;
}
function diffGt(numA, numB, threshold) {
return Math.abs(numA - numB) > threshold
}
function diffLt(numA, numB, threshold) {
return Math.abs(numA - numB) < threshold
}
function isDistinct(colorA, colorB) {
const cA = getColorAsFraction(colorA);
const cB = getColorAsFraction(colorB);
const thres1 = DISTINCT_THRESHOLD;
const thres2 = 0.03;
if (diffGt(cA.red, cB.red, thres1) || diffGt(cA.green, cB.green, thres1) || diffGt(cA.blue, cB.blue, thres1)) {
if (diffLt(cA.red, cA.green, thres2) && diffLt(cA.red, cA.blue, thres2)) {
if (diffLt(cB.red, cB.green, thres2) && diffLt(cB.red, cB.blue, thres2)) {
return false
}
}
return true
}
return false
}
function sortColorCounts(colorCount) {
return _(colorCount)
.sortBy(obj => obj.count)
.reverse()
.value();
}
function getRgb(image, x, y) {
return {
red: image.get(x, y, 0),
green: image.get(x, y, 1),
blue: image.get(x, y, 2),
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment