Commit b88d6d32 by Иван Кубота

better detection, skin tone subtract, tuning

parent 0bbd229b
......@@ -281,14 +281,48 @@ function hsvToRgb(h, s, v) {
return [ r * 255, g * 255, b * 255 ];
}
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* @param Number r The red color value
* @param Number g The green color value
* @param Number b The blue color value
* @return Array The HSL representation
*/
function rgbToHsl(r, g, b) {
r /= 255, g /= 255, b /= 255;
var extractColors = function( ctx, where, k, production){
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
var points = [].concat.apply( [], where.map( function( polygon ){
return triangle.apply( null, polygon );
} ) );
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [ h, s, l ];
}
var colorsFromPoints = function(points, ctx, where, k, production, notThis, partName) {
var deltaSkin = 15;
if(partName === 'blush')
deltaSkin = 8;
if(partName === 'eyes')
deltaSkin = 15;
var minX = Infinity, minY = Infinity,
maxX = -Infinity, maxY = -Infinity;
......@@ -305,13 +339,14 @@ var extractColors = function( ctx, where, k, production){
data = imageData.data;
var r, g, b;
var colors = [];
var colorsRaw = [];
if( !production ){
var debugPixels = document.getElementById( 'ccolors' ).getContext( '2d' );
var debugData = debugPixels.getImageData( 0, 0, 100, 480 ),
dd = debugData.data;
}
var pt = ( extracting * 100 * 120 * 4 )
var colors = [];
for( var i = 0, _i = points.length; i < _i; i++ ){
var p = points[ i ], pointer = ( p[ 0 ] - minX + ( p[ 1 ] - minY ) * w ) * 4;
......@@ -343,8 +378,50 @@ var extractColors = function( ctx, where, k, production){
//data[ pointer + 2 ] = 255
colors.push( rgbToLab( r, g, b ) );
if(notThis){
var near = false, lab = rgbToLab(r,g,b);
for( var j = 0, _j = notThis.length; j < _j; j++ ){
var notThi = notThis[ j ];
if(labDistance(notThi.hsv, lab)<deltaSkin){
near = true;
break;
}
}
if(near === false){
colorsRaw.push( { rgb: [ r, g, b ], hsl: rgbToHsl( r, g, b ) } );
}
}else{
colorsRaw.push( { rgb: [ r, g, b ], hsl: rgbToHsl( r, g, b ) } );
}
// colors.push(rgbToLab(r,g,b));
}
var bucketsCount = 100; var buckets = [];
for( var i = 0; i < bucketsCount; i++ ){
buckets.push(0);
}
var minL = Infinity, maxL = 0.8;
for( var i = 0, _i = colorsRaw.length; i < _i; i++ ){
var colorsRawElement = colorsRaw[ i ],
L = colorsRawElement.hsl[2];
(L < minL) && (minL = L);
(L > maxL) && (maxL = L);
buckets[L*bucketsCount|0]++;
}
var dL = maxL - minL, centerL = (maxL+minL)/2;
for( var i = 0, _i = colorsRaw.length; i < _i; i++ ){
var colorsRawElement = colorsRaw[ i ],
L = colorsRawElement.hsl[2];
if(buckets[L*bucketsCount|0]>_i/bucketsCount/2)
//if( L > minL+dL/5 && L < maxL - dL/3 && L < 0.75 )
colors.push( rgbToLab.apply( null, colorsRawElement.rgb ) );
}
//rgbToLab( r, g, b )
if( !production ){
debugPixels.putImageData( debugData, 0, 0 );
}
......@@ -434,6 +511,40 @@ var extractColors = function( ctx, where, k, production){
return delta < 0.0000001 || delta > 15;
} );
};
var extractColors = function( ctx, where, k, production, notThis, partName){
if(partName === 'skin'){
var colors = where.map( function( polygon ){
return colorsFromPoints(triangle.apply( null, polygon ), ctx, where, k, production, notThis, partName);
} );
var min = Infinity, max = -Infinity;
var colors = [].concat.apply([], colors);
for( var i = 0, _i = colors.length; i < _i; i++ ){
var color = colors[ i ].color;
min = Math.min(min, color[0]);
max = Math.max(max, color[0]);
}
var delta = max-min;
colors = colors.filter(function(c) {
return c.color[0] >= min + delta/10 && c.color[0] <= max + delta/10;
});
var l = 0, a = 0, b = 0;
for( var i = 0, _i = colors.length; i < _i; i++ ){
var color1 = colors[ i ].color;
l+=color1[0]; a+=color1[1]; b+=color1[2];
}
l/= _i; a /= _i; b /= _i;
return [{color: [l,a,b]}];
}
var points = [].concat.apply( [], where.map( function( polygon ){
return triangle.apply( null, polygon );
} ) );
return colorsFromPoints(points, ctx, where, k, production, notThis, partName);
};
var colorSort = function(a,b){return (-(a[2]-b[2]))+(-(a[1]-b[1])/2)};
var similarColor = 5;//0.05;
var abs = Math.abs, min = Math.min;
......@@ -458,9 +569,9 @@ function rgbToHex(r, g, b) {
return ((r << 16) | (g << 8) | b).toString(16);
}
var binds = [];
var extractor = function(partName, ctx, from, k, production) {
var extractor = function(partName, ctx, from, k, production, notThis) {
extracting++;
var colors = extractColors(ctx, from, k, production)
var colors = extractColors(ctx, from, k, production, notThis, partName)
.map(function(m) {
return {color: labToRgb.apply(null, m.color).map(function(a){return a|0}), hsv: m.color, distance: m.distance};
});
......@@ -471,18 +582,67 @@ var extractor = function(partName, ctx, from, k, production) {
binds = [];
if(production){
var mean = 0, count = colors.length;
for( var i = 0, _i = colors.length; i < _i; i++ ){
var color = colors[ i ];
mean+=color.hsv[0];//L
}
mean /= count;
if(partName === 'eyes' || partName === 'lips'){
if(mean>20){
colors = colors.filter(function(c) {
return c.hsv[0] > 10;
})
}
if(mean<88){
colors = colors.filter(function(c) {
return c.hsv[0] < 90;
})
}
}
if(partName === 'skin'){
colors = colors.filter(function(c) {
return c.hsv[0] > mean-10;
});
count = colors.length;
var mean = [0,0,0];
for( var i = 0, _i = colors.length; i < _i; i++ ){
var color = colors[ i ];
mean[0]+=color.hsv[0];//L
mean[1]+=color.hsv[1];//L
mean[2]+=color.hsv[2];//L
}
mean[0] /= count;
mean[1] /= count;
mean[2] /= count;
colors = [{color: labToRgb.apply(null, mean), hsv: mean}]
}
if(production !== 0 && production !== void 0){
var clrs = document.getElementById(partName+'-bg').querySelector('.colors');
clrs && clrs.parentNode.removeChild(clrs);
D.div({cls: 'colors', renderTo: document.getElementById(partName+'-bg') },
colors.map(function(m){
return D.div({style: 'background:rgb('+m.color.join(',')+');width:'+100/colors.length+'%', cls: 'color-preview'},
'L: '+Math.floor(m.hsv[0]),D.h('br'),
'A: '+Math.round(m.hsv[1]), D.h('br'),
'B: '+Math.round(m.hsv[2])
'B: '+Math.round(m.hsv[2]), D.h('br'),
(notThis||[]).map(function(c) {
return labDistance(c.hsv, m.hsv).toFixed(2);
}).join('~')
);
})
);
loadJSONcollection(partName, colors);
if(production === 2){
return colors;
}
if(partName[0] !== '_'){
loadJSONcollection( partName, colors );
}
return;
}
......@@ -493,7 +653,11 @@ var extractor = function(partName, ctx, from, k, production) {
'L: '+Math.floor(m.hsv[0]),D.h('br'),
'A: '+Math.round(m.hsv[1]), D.h('br'),
'B: '+Math.round(m.hsv[2])
'B: '+Math.round(m.hsv[2]), D.h('br'),
(notThis||[]).map(function(c) {
debugger
return labDistance(c, m.hsv);
})
/*'H: '+Math.floor(m.hsv[0]*360),D.h('br'),
'S: '+Math.round(m.hsv[1]*100)+'%', D.h('br'),
'V: '+Math.round(m.hsv[2]*100)+'%'*/
......@@ -501,7 +665,8 @@ var extractor = function(partName, ctx, from, k, production) {
}),
function(draw) {
getData(partName, function(res){
if(!res)
return;
var b = store.sub( [ 'threshold', 'maxCount', 'maxThreshold' ], function( threshold, maxCount, maxThreshold ){
var list = [];
......
......@@ -17,41 +17,126 @@ var FaceMarks = function(landmarks) {
var marks = this.marks = {
cheek: {
left: {
},
right: {
}
left: {},
right: {}
},
lips: {
top: {
},
bottom: {
}
top: {},
bottom: {}
},
brows: {
left: {}, right: {}
},
eyes: {
left: {}, right: {}
},
skin: {
top: {},
underEye: {left: [], right: []},
underNose: {left: [], right: []},
chin: []
}
};
// CHEEKS
// CHEEKS
marks.cheek.left = this.cheek(nose[3], toPoint(jaw[0]).middle(toPoint(jaw[1])), mouth[0]);
marks.cheek.right = this.cheek(nose[3], toPoint(jaw[15]).middle(toPoint(jaw[16])), mouth[6]);
// LIPS
marks.lips.top = this.lip(mouth[2], mouth[4], mouth[13], mouth[15], true);
marks.lips.bottom = this.lip(mouth[10], mouth[8], mouth[19], mouth[17], true);
// EYES
marks.eyes.left = this.eye(leftBrow[0], leftBrow[3], leftEye[0], leftEye[2], leftEye[3], leftEye[4]);
marks.eyes.right = this.eye(rightBrow[4], rightBrow[1], rightEye[3], rightEye[1], rightEye[0], rightEye[5]);
// SKIN
marks.skin.top = this.topSkin(leftBrow[2], rightBrow[2], nose[0]);
marks.skin.underEye = this.underEyeSkin(nose[2], marks.eyes, marks.cheek );
marks.skin.underNose = this.undeNoseSkin(mouth[2], mouth[4], mouth[1], mouth[5], marks.eyes, marks.cheek );
marks.skin.chin = this.chinSkin(mouth[11], mouth[7], jaw[8] );
};
FaceMarks.prototype = {
chinSkin: function(m1, m2, j1) {
m1 = toPoint(m1);
m2 = toPoint(m2);
j1 = toPoint(j1);
var base = [
m1.lerp(j1,2/5),
m2.lerp(j1,2/5),
m1.lerp(m2,0.5).lerp(j1, 5/6),
];
return [base[0].lerp(base[1], 3/2), base[1].lerp(base[0], 3/2), base[2]]
},
undeNoseSkin: function(m1, m2, m3, m4, eye, cheek) {
m1 = toPoint(m1);
m2 = toPoint(m2);
m3 = toPoint(m3);
m4 = toPoint(m4);
return {
left: [
m1.lerp(cheek.left.bonus2[1], 2/3),
m1.lerp(cheek.left.bonus2[1], 1/3),
cheek.left.bonus2[2].lerp(m3, 0.5)
],
right: [
m2.lerp(cheek.right.bonus2[1], 2/3),
m2.lerp(cheek.right.bonus2[1], 1/3),
cheek.right.bonus2[2].lerp(m4, 0.5)
],
}
},
underEyeSkin: function(n1, eye, cheek) {
n1 = toPoint(n1);
var p0 = n1.lerp(eye.left.t3[1],0.5);
var p1 = n1.lerp(eye.right.t3[1],0.5);
return {
left: [
p0,
p0.lerp(cheek.left.bonus2[1], 0.5),
p0.lerp(cheek.left.bonus2[1], 0.5).add(
cheek.left.bonus2[1].subClone(cheek.left.bonus2[2]).mul(-0.5)
)
],
right: [
p1,
p1.lerp(cheek.right.bonus2[1], 0.5),
p1.lerp(cheek.right.bonus2[1], 0.5).add(
cheek.right.bonus2[1].subClone(cheek.right.bonus2[2]).mul(-0.5)
)
],
}
},
topSkin: function(b1,b2, n1) {
b1 = toPoint(b1);
b2 = toPoint(b2);
n1 = toPoint(n1);
var center = b1.middle(b2);
var upVector = n1.subClone(center);
return {
left: [
b1.subClone(upVector.mulClone(2/3)),
center.subClone(upVector.mulClone(17/30)).add(b1.lerp(b2,1).normalize().mul(-2)),
center
],
right: [
b2.subClone(upVector.mulClone(2/3)),
center.subClone(upVector.mulClone(17/30)).add(b1.lerp(b2,1).normalize().mul(2)),
center
],
}
},
eye: function(b1,b2, e1,e2,e3,e4) {
b1 = toPoint(b1),
b2 = toPoint(b2),
......@@ -59,29 +144,49 @@ FaceMarks.prototype = {
e2 = toPoint(e2),
e3 = toPoint(e3),
e4 = toPoint(e4);
return {
t1: [
var t1, t2, t3, t4;
var eyes = {
t1: t1 = [
b2.lerp(e2, 2/3),
b1.lerp(e1, 2/3),
b1.lerp(e1, 1/3)
],
t2: [
t2: t2 = [
b1.lerp(e1, 0.95/3),
b1.lerp(b2, 0.5).lerp(e1, 1/3),
b1.lerp(b2, 0.5).lerp(e1, 1.4/3)
],
t3: [
t3: t3 = [
e2.lerp(e3, 2),
e2.lerp(e4, 1.65),
e2.lerp(e4, 2.35)
e2.lerp(e4, 2.45)
],
t4: [
t4: t4 = [
e2.lerp(e4, 1.65),
e2.lerp(e4, 2.35),
e2.lerp(e4, 2.45),
b2.lerp(e1, 1.5)
]
}
};
eyes.t5 = [
t1[1].lerp(t1[0],4/5),
t1[2].lerp(t1[0],5/4),
e2.lerp(e4, 1.65).lerp(
e2.lerp(e3, 2), 1.3)
//t3[0].//lerp(t1[0],1/4)
];
eyes.t6 = [
t1[1].lerp(t1[2],1/2),
t1[0].lerp(t1[1],3/2),
t4[2].lerp(t1[1],2/3)
];
var p = t3[0].lerp(t3[2], 0.5);
eyes.t7 = [
p,
t3[1].lerp(p, 2),
t3[1].lerp(t3[0], 1.4)
];
return eyes;
},
cheek: function(nose, jaw, mouthSide) {
......
......@@ -34,6 +34,7 @@ app.get('/bbt_face_recognition', (req, res) => res.sendFile(path.join(viewsDir,
app.get('/batch_face_landmarks', (req, res) => res.sendFile(path.join(viewsDir, 'batchFaceLandmarks.html')))
app.get('/batch_face_recognition', (req, res) => res.sendFile(path.join(viewsDir, 'batchFaceRecognition.html')))
app.get('/demo', (req, res) => res.sendFile(path.join(viewsDir, 'demo.html')));
app.get('/demo2', (req, res) => res.sendFile(path.join(viewsDir, 'demo2.html')));
app.get('/all', (req, res) => res.sendFile(path.join(viewsDir, 'all.html')));
app.post('/fetch_external_image', async (req, res) => {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment