Created
August 25, 2016 13:46
-
-
Save joshhunt/cc7eff65e80d66236301e0d58cf0588a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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