Skip to content

Commit 245d1f2

Browse files
committed
Version one that read the camera finds average color in the scene and the closest basic color
1 parent ae7775c commit 245d1f2

File tree

9 files changed

+795
-0
lines changed

9 files changed

+795
-0
lines changed

index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>NFC Color</title>
5+
<link rel="stylesheet" type="text/css" href="shared/style/buttons.css">
6+
</head>
7+
<body>
8+
<div>
9+
<a role="button" href="write.html">Write</a>
10+
<a role="button" href="read.html">Read</a>
11+
</div>
12+
</body>
13+
</html>

js/camera.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
var Camera = {
2+
ANALYSE_INTERVAL: 500,
3+
4+
start: function(elementToAppendTo){
5+
var self = this;
6+
this.createWebcamVideo(elementToAppendTo,function(video){
7+
self.startVideoTracking(video, self.analyseFrame, self.ANALYSE_INTERVAL);
8+
});
9+
},
10+
11+
createWebcamVideo: function(elementToAppendTo, func){
12+
if(typeof elementToAppendTo === "string"){
13+
elementToAppendTo = document.getElementById(elementToAppendTo);
14+
}
15+
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||navigator.mozGetUserMedia || navigator.msGetUserMedia;
16+
window.URL = window.URL || window.webkitURL;
17+
navigator.getUserMedia({video: true},
18+
function(localMediaStream) {
19+
var video = document.createElement("video");
20+
video.autoplay = true;
21+
video.src = window.URL.createObjectURL(localMediaStream);
22+
elementToAppendTo.appendChild(video);
23+
func(video);
24+
}, function(error) {
25+
console.log(error);
26+
}
27+
);
28+
},
29+
30+
startVideoTracking: function(video, analyseFrameFunc, analyseInterval){
31+
if(!video || !analyseFrameFunc){
32+
throw "Need video, grid and analyseFrameFunc parameters!";
33+
return;
34+
}
35+
if(!analyseInterval){
36+
analyseInterval = 1000;
37+
}
38+
video.addEventListener("canplay",function(){
39+
var videoWidth = video.videoWidth;
40+
var videoHeight = video.videoHeight;
41+
var canvasWidth = videoWidth;// > 320 ? 320 : videoWidth;
42+
var canvasHeight = videoHeight;// > 240 ? 240 : videoHeight;
43+
var imageCnvs = document.createElement("canvas");
44+
imageCnvs.width = canvasWidth;
45+
imageCnvs.height = canvasHeight;
46+
var imageCtx = imageCnvs.getContext("2d");
47+
var currentImageData = imageCtx.createImageData(canvasWidth, canvasHeight);
48+
49+
window.analyseLoop = setInterval( function(){
50+
imageCtx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, canvasWidth, canvasHeight);
51+
//var deSaturated = deSaturate(greyscaleCtx.getImageData(0, 0, canvasWidth, canvasHeight));
52+
//currentImageData = deSaturated.pop();
53+
analyseFrameFunc(imageCtx.getImageData(0, 0, canvasWidth, canvasHeight));
54+
55+
}, analyseInterval);
56+
});
57+
},
58+
59+
analyseFrame: function(imageData){
60+
var average = ColorUtils.averageColor(imageData);
61+
var closest = average.closest();
62+
document.getElementById("average").style.cssText = "background-color: "+average.css;
63+
document.getElementById("closest").style.cssText = "background-color: "+closest.css;
64+
}
65+
}
66+
67+

js/color.js

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
function RGB(r,g,b,name){
2+
this.r = r;
3+
this.g = g;
4+
this.b = b;
5+
if(name) this.name = name;
6+
this.css = 'rgb('+r+','+g+','+b+')';
7+
}
8+
9+
RGB.prototype.toXYZ = function() {
10+
var var_R = this.r / 255; //R from 0 to 255
11+
var var_G = this.g / 255; //G from 0 to 255
12+
var var_B = this.b / 255; //B from 0 to 255
13+
14+
if (var_R > 0.04045) var_R = Math.pow(((var_R + 0.055) / 1.055), 2.4);
15+
else var_R = var_R / 12.92;
16+
if (var_G > 0.04045) var_G = Math.pow(((var_G + 0.055) / 1.055), 2.4);
17+
else var_G = var_G / 12.92;
18+
if (var_B > 0.04045) var_B = Math.pow(((var_B + 0.055) / 1.055), 2.4);
19+
else var_B = var_B / 12.92;
20+
21+
var_R = var_R * 100;
22+
var_G = var_G * 100;
23+
var_B = var_B * 100;
24+
25+
//Observer. = 2°, Illuminant = D65
26+
var X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805;
27+
var Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722;
28+
var Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505;
29+
30+
return new XYZ(X, Y, Z);
31+
}
32+
33+
RGB.prototype.toLAB = function(){
34+
return this.toXYZ().toLAB();
35+
}
36+
37+
RGB.prototype.closest = function(){
38+
return this.toLAB().closest();
39+
}
40+
41+
function XYZ(x,y,z){
42+
this.x = x;
43+
this.y = y;
44+
this.z = z;
45+
}
46+
47+
var ref_X = 95.047; //Observer= 2°, Illuminant= D65
48+
var ref_Y = 100.000;
49+
var ref_Z = 108.883;
50+
var const1 = 1.00 / 3;
51+
var const2 = 16.00 / 116;
52+
53+
XYZ.prototype.toLAB = function(){
54+
var var_X = this.x / ref_X;
55+
var var_Y = this.y / ref_Y;
56+
var var_Z = this.z / ref_Z;
57+
58+
if (var_X > 0.008856) var_X = Math.pow(var_X, const1);
59+
else var_X = (7.787 * var_X) + const2;
60+
if (var_Y > 0.008856) var_Y = Math.pow(var_Y, const1);
61+
else var_Y = (7.787 * var_Y) + const2;
62+
if (var_Z > 0.008856) var_Z = Math.pow(var_Z, const1);
63+
else var_Z = (7.787 * var_Z) + const2;
64+
65+
var L = (116 * var_Y) - 16;
66+
var A = 500 * (var_X - var_Y);
67+
var B = 200 * (var_Y - var_Z);
68+
return new LAB(L, A, B);
69+
70+
}
71+
72+
function LAB(l,a,b){
73+
this.l = l;
74+
this.a = a;
75+
this.b = b;
76+
}
77+
78+
LAB.prototype.delta =function(lab2){
79+
return ColorUtils.calculateDeltaE(this.l,this.a,this.b,lab2.l,lab2.a,lab2.b);
80+
}
81+
82+
LAB.prototype.closest = function(){
83+
var closest = -1;
84+
var minDiff = 999999999;
85+
for (var i = 0; i < ColorUtils.COLORS_LAB.length; ++i) {
86+
var diff = this.delta(ColorUtils.COLORS_LAB[i]);
87+
if (diff < minDiff) {
88+
minDiff = diff;
89+
closest = i;
90+
}
91+
}
92+
return ColorUtils.COLORS[closest];
93+
}
94+
95+
var ColorUtils = {
96+
COLORS: [
97+
new RGB(0,0,0,"black"),
98+
new RGB(255,0,0,"red"),
99+
new RGB(255,127,0,"orange"),
100+
new RGB(255,255,0,"yellow"),
101+
new RGB(0,255,0,"green"),
102+
new RGB(0,255,0,"blue"),
103+
new RGB(75,0,130,"indigo"),
104+
new RGB(143,0,255,"violet"),
105+
new RGB(255,255,255,"white")
106+
],
107+
108+
COLORS_LAB: [
109+
new RGB(0,0,0,"black").toLAB(),
110+
new RGB(255,0,0,"red").toLAB(),
111+
new RGB(255,127,0,"orange").toLAB(),
112+
new RGB(255,255,0,"yellow").toLAB(),
113+
new RGB(0,255,0,"green").toLAB(),
114+
new RGB(0,255,0,"blue").toLAB(),
115+
new RGB(75,0,130,"indigo").toLAB(),
116+
new RGB(143,0,255,"violet").toLAB(),
117+
new RGB(255,255,255,"white").toLAB()
118+
],
119+
120+
averageColor: function(imgData){
121+
var totalRed = 0;
122+
var totalGreen = 0;
123+
var totalBlue = 0;
124+
var data = imgData.data;
125+
var bytes = data.length;
126+
for (var i=0;i<bytes;i+=4){
127+
totalRed += data[i];
128+
totalGreen += data[i+1];
129+
totalBlue += data[i+2];
130+
}
131+
var len = bytes/4;
132+
return new RGB(Math.round(totalRed / len), Math.round(totalGreen / len), Math.round(totalBlue / len));
133+
},
134+
135+
calculateDeltaE: function(L1, a1, b1, L2, a2, b2) {
136+
var Lmean = (L1 + L2) / 2.0;
137+
var C1 = Math.sqrt(a1*a1 + b1*b1);
138+
var C2 = Math.sqrt(a2*a2 + b2*b2);
139+
var Cmean = (C1 + C2) / 2.0;
140+
141+
var G = ( 1 - Math.sqrt( Math.pow(Cmean, 7) / (Math.pow(Cmean, 7) + Math.pow(25, 7)) ) ) / 2;
142+
var a1prime = a1 * (1 + G);
143+
var a2prime = a2 * (1 + G);
144+
145+
var C1prime = Math.sqrt(a1prime*a1prime + b1*b1);
146+
var C2prime = Math.sqrt(a2prime*a2prime + b2*b2);
147+
var Cmeanprime = (C1prime + C2prime) / 2;
148+
149+
var h1prime = Math.atan2(b1, a1prime) + 2*Math.PI * (Math.atan2(b1, a1prime)<0 ? 1 : 0);
150+
var h2prime = Math.atan2(b2, a2prime) + 2*Math.PI * (Math.atan2(b2, a2prime)<0 ? 1 : 0);
151+
var Hmeanprime = ((Math.abs(h1prime - h2prime) > Math.PI) ? (h1prime + h2prime + 2*Math.PI) / 2 : (h1prime + h2prime) / 2);
152+
153+
var T = 1.0 - 0.17 * Math.cos(Hmeanprime - Math.PI/6.0) + 0.24 * Math.cos(2*Hmeanprime) + 0.32 * Math.cos(3*Hmeanprime + Math.PI/30) - 0.2 * Math.cos(4*Hmeanprime - 21*Math.PI/60); //ok
154+
155+
var deltahprime = ((Math.abs(h1prime - h2prime) <= Math.PI) ? h2prime - h1prime : (h2prime <= h1prime) ? h2prime - h1prime + 2*Math.PI : h2prime - h1prime - 2*Math.PI);
156+
157+
var deltaLprime = L2 - L1;
158+
var deltaCprime = C2prime - C1prime;
159+
var deltaHprime = 2.0 * Math.sqrt(C1prime*C2prime) * Math.sin(deltahprime / 2.0);
160+
var SL = 1.0 + ( (0.015*(Lmean - 50)*(Lmean - 50)) / (Math.sqrt( 20 + (Lmean - 50)*(Lmean - 50) )) );
161+
var SC = 1.0 + 0.045 * Cmeanprime;
162+
var SH = 1.0 + 0.015 * Cmeanprime * T;
163+
164+
var deltaTheta = (30 * Math.PI / 180) * Math.exp(-((180/Math.PI*Hmeanprime-275)/25)*((180/Math.PI*Hmeanprime-275)/25));
165+
var RC = (2 * Math.sqrt(Math.pow(Cmeanprime, 7) / (Math.pow(Cmeanprime, 7) + Math.pow(25, 7))));
166+
var RT = (-RC * Math.sin(2 * deltaTheta));
167+
168+
var KL = 1;
169+
var KC = 1;
170+
var KH = 1;
171+
172+
var deltaE = Math.sqrt(
173+
((deltaLprime/(KL*SL)) * (deltaLprime/(KL*SL))) +
174+
((deltaCprime/(KC*SC)) * (deltaCprime/(KC*SC))) +
175+
((deltaHprime/(KH*SH)) * (deltaHprime/(KH*SH))) +
176+
(RT * (deltaCprime/(KC*SC)) * (deltaHprime/(KH*SH)))
177+
);
178+
179+
return deltaE;
180+
}
181+
182+
};

manifest.webapp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name":"NFColor",
3+
"developer": {
4+
"name":"Cristian Vrabie",
5+
"url":"http://vrabie.info"
6+
},
7+
"description":"Playing with colors.",
8+
"icons":{
9+
"128":"/icon.png"
10+
}
11+
}

read.html

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>NFC Color Read</title>
5+
<link rel="stylesheet" type="text/css" href="shared/style/buttons.css">
6+
<link rel="stylesheet" type="text/css" href="style/nfccolor.css">
7+
<script type="text/javascript" src="js/color.js"></script>
8+
<script type="text/javascript" src="js/camera.js"></script>
9+
</head>
10+
<body>
11+
<div id="video"></div>
12+
<div id="footer">
13+
<div id="colors">
14+
<div id="average"></div>
15+
<div id="closest"></div>
16+
</div>
17+
<a role="button" href="write.html">Back</a>
18+
</div>
19+
<script type="text/javascript">
20+
Camera.start("video");
21+
</script>
22+
</body>
23+
</html>

0 commit comments

Comments
 (0)