I'm working on an art project which converts pixels of live video feed into corporate logos based on the distance (in RGB) between the colors of the two. While this functions, it gives a jittery result. Seem like some points in the color space teeter in a sort of superposition between two "closest" points. I'm attempting a sort of naive clustering solution right now because I believe any proper one will be too slow for live video. I'm wondering if anyone has any good ideas to solve this problem? I'll include my code and an example of the result. Thank you!
(imgs array is the logos)
current result: https://gifyu.com/image/fk2y
function distance(r1, g1, b1, bright1, r2, g2, b2, bright2) {
d =
((r2 - r1) * 0.3) ** 2 +
((g2 - g1) * 0.59) ** 2 +
((b2 - b1) * 0.11) ** 2 +
((bright2 - bright1) * 0.75) ** 2;
return Math.round(d);
}
function draw() {
if (x > 100 && z == true) {
video.loadPixels();
for (var y = 0; y < video.height; y++) {
for (var x = 0; x < video.width; x++) {
var index = (video.width - x - 1 + y * video.width) * 4;
var r = video.pixels[index];
var g = video.pixels[index + 1];
var b = video.pixels[index + 2];
var bright = (r + g + b) / 3;
let least = 9999999;
for (var i = 0; i < imgs.length; i++) {
if (
distance(
imgs[i].r,
imgs[i].g,
imgs[i].b,
imgs[i].bright,
r,
g,
b,
bright
) < least
) {
least = distance(
imgs[i].r,
imgs[i].g,
imgs[i].b,
imgs[i].bright,
r,
g,
b,
bright
);
place = imgs[i].img;
}
}
image(place, round(x * vScale), y * vScale, vScale, vScale);
}
}
}
}
As a part time project I am working on some geometry utilities and have come across a relatively simple question that seems to have a not so simple solution.
The problem involves EPSILON being too small for the problem. To see if two triangle are similar I workout the 3 interior angles in the form of their cosines for each triangle and then sort them. I then test Math.abs(t1[0]-t2[0]) < EPSILON where t1 is the one triangle and t2 the other each containing the three angles.
I am getting about a 20% - 80% failure rate on triangles I know to be similar. When I bring EPSILON to a larger value, for example still a very small 0.0000001 there is no failure ( well not in the time I have let the tests run).
Below is the extracted relevant triangle function and I have also included the testing code as a demo below that. Click the button and its runs tests and shows the results. The triangles are randomly generated. Every so often two similar triangle are created of which about half are exact copies and the rest are a copy but scaled, mirrored, rotated and vec order shuffled while still maintaining the similarity
I would like to know how to calculate a reasonable EPSILON that will reduce the incorrect results but keep the system as accurate as possible?
Though there is also the possibility that there is another error in the test code which I will continue to check.
const EPSILON = Number.EPSILON
function Triangle(p1,p2,p3){
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
}
Triangle.prototype.p1 = undefined;
Triangle.prototype.p2 = undefined;
Triangle.prototype.p3 = undefined;
Triangle.prototype.isSimilar = function(triangle){
var a1,b1,c1,a2,b2,c2,aa1,bb1,cc1,aa2,bb2,cc2; //
var t1 = [];
var t2 = [];
var sortF = function(a,b){ return a-b };
// get the length squared and length of each side
a1 = Math.sqrt(aa1 = Math.pow(this.p1.x - this.p2.x, 2) + Math.pow(this.p1.y - this.p2.y, 2));
b1 = Math.sqrt(bb1 = Math.pow(this.p2.x - this.p3.x, 2) + Math.pow(this.p2.y - this.p3.y, 2));
c1 = Math.sqrt(cc1 = Math.pow(this.p3.x - this.p1.x, 2) + Math.pow(this.p3.y - this.p1.y, 2));
a2 = Math.sqrt(aa2 = Math.pow(triangle.p1.x - triangle.p2.x, 2) + Math.pow(triangle.p1.y - triangle.p2.y, 2));
b2 = Math.sqrt(bb2 = Math.pow(triangle.p2.x - triangle.p3.x, 2) + Math.pow(triangle.p2.y - triangle.p3.y, 2));
c2 = Math.sqrt(cc2 = Math.pow(triangle.p3.x - triangle.p1.x, 2) + Math.pow(triangle.p3.y - triangle.p1.y, 2));
// get the cosin of each angle for both triangle
t1[0] = (cc1 - (aa1 + bb1)) / (-2 * a1 * b1);
t1[1] = (aa1 - (cc1 + bb1)) / (-2 * c1 * b1);
t1[2] = (bb1 - (cc1 + aa1)) / (-2 * c1 * a1);
t2[0] = (cc2 - (aa2 + bb2)) / (-2 * a2 * b2);
t2[1] = (aa2 - (cc2 + bb2)) / (-2 * c2 * b2);
t2[2] = (bb2 - (cc2 + aa2)) / (-2 * c2 * a2);
t1.sort(sortF);
t2.sort(sortF);
if(Math.abs(t1[0] - t2[0]) < EPSILON && Math.abs(t1[1] - t2[1]) < EPSILON && Math.abs(t1[2] - t2[2]) < EPSILON){
return true;
}
return false;
}
function Vec(x,y){
this.x = x;
this.y = y;
}
Vec.prototype.x = undefined;
Vec.prototype.y = undefined;
UPDATE
Some more information.
Failed similar triangle using cosine of angles EPSILON : 2.220446049250313e-16
Failed Triangles ID : 94
Method : compare cosine of angles
Both Compare T1 to T2 and T2 to T1 failed
Both Triangles known to be similare
Triangle 1
p1.x = -149241116087155.97;
p1.y = -1510074922190599.8;
p2.x = -2065214078816255.8;
p2.y = 6756872141691895;
p3.x = -7125027429739231;
p3.y = -5622578541875555;
Triangle 2
p1.x = -307440480802857.2;
p1.y = -404929352172871.56;
p2.x = -3020163594243123;
p2.y = -355583557775981.75;
p3.x = 595422457974710.8;
p3.y = 2291176238828451.5;
Compare T1 to T2 Result : false
Computed values
Triangle 1 length of side and square length
length a : 8486068945686473 squared : 7.201336615094433e+31
length b : 13373575078230092 squared : 1.78852510373057e+32
length c : 8097794805726894 squared : 6.557428071565746e+31
Unsorted cosines C is angle opposite side c
cosine C : 0.8163410767815653
cosine A : 0.7960251614312384
cosine B : -0.30024590551189423
ratio a : undefined
ratio b : undefined
ratio c : undefined
Triangle2
length a : 2713171888697380.5 squared : 7.36130169761771e+30
length b : 4480825808030667.5 squared : 2.0077799921913682e+31
length c : 2843263414467020.5 squared : 8.08414684404666e+30
Unsorted cosines C is angle opposite side c
cosine C : 0.7960251614312384
cosine A : 0.8163410767815651
cosine B : -0.3002459055118942
Compare T2 to T1 Result : false
Triangle1
Computed values
Triangle 1 length of side and square length
length a : 2713171888697380.5 squared : 7.36130169761771e+30
length b : 4480825808030667.5 squared : 2.0077799921913682e+31
length c : 2843263414467020.5 squared : 8.08414684404666e+30
Unsorted cosines C is angle opposite side c
cosine a : 0.7960251614312384
cosine b : 0.8163410767815651
cosine c : -0.3002459055118942
ratio a : undefined
ratio b : undefined
ratio c : undefined
Triangle2
length a : 8486068945686473 squared : 7.201336615094433e+31
length b : 13373575078230092 squared : 1.78852510373057e+32
length c : 8097794805726894 squared : 6.557428071565746e+31
cosine a : 0.8163410767815653
cosine b : 0.7960251614312384
cosine c : -0.30024590551189423
UPDATE 2
Results output and a bug fix (apologies #lhf I did not sqrt epsilon I was still using the original constant)
This shows the results of tests on the same set of triangles. Inconsistency means that comparing triangle 1 to triangle 2 is a different result than triangle 2 to 1. Incorrect Means that two known similar triangles failed and Incorrect Inconsistency means that two known similar triangle failed one test and passed the other.
Using the ratios of lengths gave the worst result but using cosine was not much better apart from the Incorrect Inconsistency similar triangles which had a very high rate of inconsistency between compare t1 to t2 and t2 to t1 using the ratio of length. But that makes sense are the magnitude of the ratios will vary greatly depending on which order the test is done.
As you can see using the square root of EPSILON completely eliminated the error for both methods.
If lhf wishes to put the sqrt(epsilon) comment as an answer I will accept that as an answer. And thanks to everyone for their input and I have some further reading thanks to Salix
======================================
Default EPSILON : 2.220446049250313e-16
======================================
Via cosine of angles
All Inconsistency failed : 0 of 10000
Similar Incorrect failed : 1924 of 5032
Similar Incorrect Inconsistency failed : 0 of 5032
======================================
Via ratio of lengths
All Inconsistency failed : 1532 of 10000
Similar Incorrect failed : 2082 of 5032
Similar Incorrect Inconsistency failed : 1532 of 5032
======================================
Squaring EPSILON : 1.4901161193847656e-8
======================================
Via cosine of angles
All Inconsistency failed : 0 of 10000
Similar Incorrect failed : 0 of 5032
Similar Incorrect Inconsistency failed : 0 of 5032
======================================
Via ratio of lengths
All Inconsistency failed : 0 of 10000
Similar Incorrect failed : 0 of 5032
Similar Incorrect Inconsistency failed : 0 of 5032
const EPSILON = Number.EPSILON
function Triangle(p1,p2,p3){
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
}
Triangle.prototype.p1 = undefined;
Triangle.prototype.p2 = undefined;
Triangle.prototype.p3 = undefined;
Triangle.prototype.isSimilar = function(triangle){
var a1,b1,c1,a2,b2,c2,aa1,bb1,cc1,aa2,bb2,cc2; //
var t1 = [];
var t2 = [];
var sortF = function(a,b){ return a-b };
// get the length squared and length of each side
a1 = Math.sqrt(aa1 = Math.pow(this.p1.x - this.p2.x, 2) + Math.pow(this.p1.y - this.p2.y, 2));
b1 = Math.sqrt(bb1 = Math.pow(this.p2.x - this.p3.x, 2) + Math.pow(this.p2.y - this.p3.y, 2));
c1 = Math.sqrt(cc1 = Math.pow(this.p3.x - this.p1.x, 2) + Math.pow(this.p3.y - this.p1.y, 2));
a2 = Math.sqrt(aa2 = Math.pow(triangle.p1.x - triangle.p2.x, 2) + Math.pow(triangle.p1.y - triangle.p2.y, 2));
b2 = Math.sqrt(bb2 = Math.pow(triangle.p2.x - triangle.p3.x, 2) + Math.pow(triangle.p2.y - triangle.p3.y, 2));
c2 = Math.sqrt(cc2 = Math.pow(triangle.p3.x - triangle.p1.x, 2) + Math.pow(triangle.p3.y - triangle.p1.y, 2));
// get the cosin of each angle for both triangle
t1[0] = (cc1 - (aa1 + bb1)) / (-2 * a1 * b1);
t1[1] = (aa1 - (cc1 + bb1)) / (-2 * c1 * b1);
t1[2] = (bb1 - (cc1 + aa1)) / (-2 * c1 * a1);
t2[0] = (cc2 - (aa2 + bb2)) / (-2 * a2 * b2);
t2[1] = (aa2 - (cc2 + bb2)) / (-2 * c2 * b2);
t2[2] = (bb2 - (cc2 + aa2)) / (-2 * c2 * a2);
t1.sort(sortF);
t2.sort(sortF);
if(Math.abs(t1[0] - t2[0]) < EPSILON && Math.abs(t1[1] - t2[1]) < EPSILON && Math.abs(t1[2] - t2[2]) < EPSILON){
return true;
}
return false;
}
function Vec(x,y){
this.x = x;
this.y = y;
}
Vec.prototype.x = undefined;
Vec.prototype.y = undefined;
var iterations = 1000; // number of tests
var presentSimilar = 1/2; // odds of similar triangle
var presentSuperSimilar = 1/2; // odds of triangles being identical
var presentInfinity = 0;//1/20; // odds of a divide by zero
var presentDegenerate = 0;//1/100; // odds of a degenerate triangle can be colinear or degenerate right triangle
var v; // temp for swap
var xdx,xdy,ydx,ydy; // vars for rotation
var copyVec = [["p1","p2","p3"],["p2","p3","p1"],["p3","p1","p2"]]; // pick a vec for selecting vecs
// the triangles for testing;
var tri1 = new Triangle(new Vec(0,0), new Vec(0,0), new Vec(0,0));
var tri2 = new Triangle(new Vec(0,0), new Vec(0,0), new Vec(0,0));
// max Random
function rMax(){
return ((Math.random()*2)-1) * Number.MAX_SAFE_INTEGER;
}
// rotate function
function rotate(vec){
var x = vec.x;
var y = vec.y;
vec.x = x * xdx + y * ydx;
vec.y = x * xdy + y * ydy;
};
function translateVec(vec,x,y){
vec.x += x;
vec.y += y;
}
function translateTriangle(tri,x,y){
translateVec(tri.p1);
translateVec(tri.p2);
translateVec(tri.p3);
}
// make infinite vec to simulate the result of a divide by zero
function doInfinity(vec){
if(Math.random() < presentInfinity){
if(Math.random() < 0.5){
vec.x = Infinity;
vec.y = Infinity;
}else{
vec.x = -Infinity;
vec.y = -Infinity;
}
}
}
// create a random vector;
function randomVec(vec){
vec.x = rMax();
vec.y = rMax();
doInfinity(vec);
}
// create a random triangle
function randomTriangle(tri){
var p,r;
randomVec(tri.p1);
randomVec(tri.p2);
randomVec(tri.p3);
if(Math.random() < presentDegenerate){
r = Math.random();
if(r < 1/3){ // Degenerate right triangle
p = copyVec[Math.floor(Math.random()*3)]; // get two vec to be at the same location
tri[p[0]].x = tri[p[1]].x;
tri[p[0]].y = tri[p[1]].y;
}else // Degenerate colinear triangle
if(r < 2/3){
p = copyVec[Math.floor(Math.random()*3)]; // get two vec to be at the same location
r = Math.random();
tri[p[0]].x = (tri[p[1]].x - tri[p[2]].x) * r + tri[p[2]].x;
tri[p[0]].y = (tri[p[1]].y - tri[p[2]].y) * r + tri[p[2]].y;
}else{ // degenerate undimentioned triangle. Has not area
tri.p1.x = tri.p2.x = tri.p3.x;
tri.p1.y = tri.p2.y = tri.p3.y;
}
}
}
function runTest(){
var result1,result2,mustBeSimilar;
var countSimilar = 0;
var countNorm = 0;
var error1 = 0;
var error2 = 0;
for(var i = 0; i < iterations; i ++){
randomTriangle(tri1);
if(Math.random() < presentSimilar){
mustBeSimilar = true;
countSimilar += 1;
tri2.p1.x = tri1.p1.x;
tri2.p1.y = tri1.p1.y;
tri2.p2.x = tri1.p2.x;
tri2.p2.y = tri1.p2.y;
tri2.p3.x = tri1.p3.x;
tri2.p3.y = tri1.p3.y;
if(Math.random() >= presentSuperSimilar){
if(Math.random() < 0.5){ // swap two
v = tri2.p1;
tri2.p1 = tri2.p2;
tri2.p2 = v;
}
if(Math.random() < 0.5){ // swap two
v = tri2.p2;
tri2.p2 = tri2.p3;
tri2.p3 = v;
}
if(Math.random() < 0.5){ // swap two
v = tri2.p1;
tri2.p1 = tri2.p3;
tri2.p3 = v;
}
// scale and or mirror the second triangle
v = Math.random() * 2 - 1;
tri2.p1.x *= v;
tri2.p1.y *= v;
tri2.p2.x *= v;
tri2.p2.y *= v;
tri2.p3.x *= v;
tri2.p3.y *= v;
// rotate the triangle
v = (Math.random()- 0.5) * Math.PI * 4;
ydy = xdx = Math.cos(v);
ydx = -(xdy = Math.sin(v));
rotate(tri2.p1);
rotate(tri2.p2);
rotate(tri2.p3);
}
}else{
randomTriangle(tri2);
mustBeSimilar = false;
}
countNorm += 1;
result1 = tri1.isSimilar(tri2);
result2 = tri2.isSimilar(tri1);
if(result1 !== result2){
error1 += 1;
}
if(mustBeSimilar && (!result1 || !result2)){
error2 += 1;
}
for(var j = 0; j < 10; j++){
translateTriangle(tri1,Math.random(),Math.random());
translateTriangle(tri2,Math.random(),Math.random());
if(mustBeSimilar){
countSimilar += 1;
}
countNorm += 1;
result1a = tri1.isSimilar(tri2);
result2a = tri2.isSimilar(tri1);
if(result1a !== result2a || result1 !== result1a || result2 !== result2a){
error1 += 1;
}
if(mustBeSimilar && (!result1a || !result2a)){
error2 += 1;
}
}
}
divResult1.textContent = "Inconsistancy result failed : "+error1 + " of "+ countNorm;
divResult2.textContent = "Incorrect result failed : "+error2 + " of "+ countSimilar
}
var button = document.createElement("input");
button.type = "button"
button.value = "Run test"
button.onclick = runTest;
var divResult1 = document.createElement("div");
var divResult2 = document.createElement("div");
document.body.appendChild(button);
document.body.appendChild(divResult1);
document.body.appendChild(divResult2);
Following willywokka's comment. You may be able to just see if there is a single scale factor.
// get the length squared and length of each side
a1 = Math.sqrt(...);
....
// Sort the values so a1 < b1 < c1, a2 < b2 < c2
if(b1 < a1) { tmp = b1; b1 = a1; a1 = tmp }
if(c1 < a1) { tmp = c1; c1 = a1; a1 = tmp }
if(c1 < b1) { tmp = c1; c1 = b1; b1 = tmp }
if(b2 < a2) { tmp = b2; b2 = a2; a2 = tmp }
if(c2 < a2) { tmp = c2; c2 = a2; a2 = tmp }
if(c2 < b2) { tmp = c2; c2 = b2; b2 = tmp }
// work out the scale factors
ka = a2 / a1;
kb = b2 / b1;
kc = c2 / c1;
if( abs(ka - kb) < epsilon && abs(kc - ka) < epsilon && abs(kc - kb) < epsilon )
// similar
else
// not similar
Rather than working with an absolute epsilon you might want one which is within x% of the value. So that the values are considered equal if ka - x% < kb < ka + x%, that is (1-x/100) ka < kb < (1+x/100) ka. Or (1-x/100) < kb/ka < (1+x/100), or abs(kb/ka) < x/100.
There is a statistically more rigorous approach to the problem. This would involve a pretty precise definition of what we mean by similar and examining the distribution of triangles. Statistical shape analysis is a poor starting point. David George Kendall did work examining the shape of triangles.
If it is really just angles, you can simply compute the three inner angles of both triangles. Just use the cosine,
cos(angle) = dot (normalized(edge[i]),normalized(edge[(i+1)%3])).
with edge[i] = p[(i+1)%3] - p[i].
So you have three cos(angle) for each triangle one and two. Then just check every permutation. A triangle has only six permutations. (http://mathworld.wolfram.com/Permutation.html)
besterr = max;
for i=1..6 perm(i) in tri1
for j=1..6 perm(j) in tri2
err = 0
for k=1..3 angle
err += abs(angletri1[perm[i,k]] - angletri2[perm[j,k]])
if (err<besterr) besterr = err;
return besterr;
Does that give your expected result? We certainly can do more efficient. But this is the brute force test algorithm. One thing to note is that it only works for triangles - any vertex permutation in a triangle is the same triangle outline. That would not be the case for a bigger polygon.
Once this works you can start experimenting. Do you get the same result for angle and cos(angle)? For err += abs(d) and err += d*d? Can you just check 2 permutations by sorting angles? Remember triangle angles sum (https://en.wikipedia.org/wiki/Sum_of_angles_of_a_triangle). What computations are redundant?
And finally: Is it really the metric you want? Are two triangles with opposite winding really similar? A huge and a tiny one?
Javascript>
If you are in the data science industry, you would be bothered if you don't have normal distribution table. I came across the article in Stackoverflow that converts z-score to probability in JavaScript. What I really want to know is the reverse calculation of this function.
/**
* #param {number} z - Number of standard deviations from the mean.
*/
function GetZPercent(z) {
// If z is greater than 6.5 standard deviations from the mean
// the number of significant digits will be outside of a reasonable
// range.
if (z < -6.5)
return 0.0;
if (z > 6.5)
return 1.0;
var factK = 1;
var sum = 0;
var term = 1;
var k = 0;
var loopStop = Math.exp(-23);
while (Math.abs(term) > loopStop) {
term = 0.3989422804 * Math.pow(-1, k) * Math.pow(z, k) / (2 * k + 1) /
Math.pow(2, k) * Math.pow(z, k + 1) / factK;
sum += term;
k++;
factK *= k;
}
sum += 0.5;
return sum;
}
I have a sense of how to convert z-score into the probability. But, I have no idea how to calculate the z-score(Standard deviation) from corresponding probability in javascript. For example, If I put in 0.95 (or 95%), I can expect to get 2.25 standard deviation. Above code gives me 95%, if I enter 2.25.
I found that this code also works. Use critz(p) to convert probability to z-score. For example we can expect 1.65 from critz(0.95) as 95% corresponds to 1.65 standard deviation in z-score.
/* The following JavaScript functions for calculating normal and
chi-square probabilities and critical values were adapted by
John Walker from C implementations
written by Gary Perlman of Wang Institute, Tyngsboro, MA
01879. Both the original C code and this JavaScript edition
are in the public domain. */
/* POZ -- probability of normal z value
Adapted from a polynomial approximation in:
Ibbetson D, Algorithm 209
Collected Algorithms of the CACM 1963 p. 616
Note:
This routine has six digit accuracy, so it is only useful for absolute
z values <= 6. For z values > to 6.0, poz() returns 0.0.
*/
var Z_MAX = 6;
function poz(z) {
var y, x, w;
if (z == 0.0) {
x = 0.0;
} else {
y = 0.5 * Math.abs(z);
if (y > (Z_MAX * 0.5)) {
x = 1.0;
} else if (y < 1.0) {
w = y * y;
x = ((((((((0.000124818987 * w
- 0.001075204047) * w + 0.005198775019) * w
- 0.019198292004) * w + 0.059054035642) * w
- 0.151968751364) * w + 0.319152932694) * w
- 0.531923007300) * w + 0.797884560593) * y * 2.0;
} else {
y -= 2.0;
x = (((((((((((((-0.000045255659 * y
+ 0.000152529290) * y - 0.000019538132) * y
- 0.000676904986) * y + 0.001390604284) * y
- 0.000794620820) * y - 0.002034254874) * y
+ 0.006549791214) * y - 0.010557625006) * y
+ 0.011630447319) * y - 0.009279453341) * y
+ 0.005353579108) * y - 0.002141268741) * y
+ 0.000535310849) * y + 0.999936657524;
}
}
return z > 0.0 ? ((x + 1.0) * 0.5) : ((1.0 - x) * 0.5);
}
/* CRITZ -- Compute critical normal z value to
produce given p. We just do a bisection
search for a value within CHI_EPSILON,
relying on the monotonicity of pochisq(). */
function critz(p) {
var Z_EPSILON = 0.000001; /* Accuracy of z approximation */
var minz = -Z_MAX;
var maxz = Z_MAX;
var zval = 0.0;
var pval;
if( p < 0.0 ) p = 0.0;
if( p > 1.0 ) p = 1.0;
while ((maxz - minz) > Z_EPSILON) {
pval = poz(zval);
if (pval > p) {
maxz = zval;
} else {
minz = zval;
}
zval = (maxz + minz) * 0.5;
}
return(zval);
}
Here is a function that does an opposite calculation (probability to z-score). This snippet allows you to input the probability and the the corresponding z-score is displayed:
function percentile_z(p) {
if (p < 0.5) return -percentile_z(1-p);
if (p > 0.92) {
if (p == 1) return Infinity;
let r = Math.sqrt(-Math.log(1-p));
return (((2.3212128*r+4.8501413)*r-2.2979648)*r-2.7871893)/
((1.6370678*r+3.5438892)*r+1);
}
p -= 0.5;
let r = p*p;
return p*(((-25.4410605*r+41.3911977)*r-18.6150006)*r+2.5066282)/
((((3.1308291*r-21.0622410)*r+23.0833674)*r-8.4735109)*r+1);
}
// I/O handling
function calc() {
var p = +document.getElementById("prob").value;
var z = percentile_z(p);
document.getElementById("z").textContent = z.toFixed(4);
}
calc();
input { width: 5em }
Probability (between 0 and 1):
<input type="number" id="prob" step="0.0001" min="0" max="1" value="0.9500" oninput="calc()"><p>
Z Score: <span id="z"></span>
For a probability of 0.95 it returns a z-score of 1.6449. See also this table as reference.
Derived from easycalculation.com
Is there a way, given two colors (let's call them first color and second color) to figure out what color the second color must be changed to in order to obtain a contrast ratio of 4.5? In other words, what amount do we need to shift the second color in order to obtain a contrast ratio of 4.5? The luminance and ratio code are as follows:
Luminance Code:
function getLuminance (rgb){
for (var i =0; i<rgb.length; i++) {
if (rgb[i] <= 0.03928) {
rgb[i] = rgb[i] / 12.92;
} else {
rgb[i] = Math.pow( ((rgb[i]+0.055)/1.055), 2.4 );
}
}
var l = (0.2126 * rgb[0]) + (0.7152 * rgb[1]) + (0.0722 * rgb[2]);
return l;
};
Ratio Code:
var ratio = 1;
var l1 = getLuminance([first_color_red.value/255, first_color_green.value/255, first_color_blue.value/255]);
var l2 = getLuminance([second_color_red.value/255, second_color_green.value/255, second_color_blue.value/255]);
if (l1 >= l2) {
ratio = (l1 + .05) / (l2 + .05);
} else {
ratio = (l2 + .05) / (l1 + .05);
}
The problem i'm facing is with the luminance calculation - just based on a ratio, it seems like it is not possible to figure out the R, G and B values as they would be three unknowns (depends on which path each of the colors takes - is it greater than 0.03928 or not...).
We can add in more constraints by considering the color brightness and color difference calculations as shown below:
Color Brightness Code:
The threshold for brightnessDifference is that it is greater than or equal to 125.
fY=((first_color_r.value * 299) + (first_color_g.value * 587) + (first_color_b.value * 114)) / 1000;
sY=((second_color_r.value * 299) + (second_color_g.value * 587) + (second_color_b.value * 114)) / 1000;
brightnessDifference = Math.abs(fY-sY);
Color Difference Code:
The threshold for colorDifference is that it is greater than or equal to 500.
colorDifference = (Math.max (second_color_r.value, first_color_r.value) - Math.min (second_color_r.value, first_color_r.value)) +
(Math.max (second_color_g.value, first_color_g.value) - Math.min (second_color_g.value, first_color_g.value)) +
(Math.max (second_color_b.value, first_color_b.value) - Math.min (second_color_b.value, first_color_b.value));
I have read the following post: How to pick good contrast RGB colors programmatically?
However, calculating complementary colors may not always solve the problem! It may not even result in a contrast ratio of 4.5 ... Any ideas on how this may be accomplished?
I'm into a 2D/3D graphic project and I'm facing a performance problem.
My algorithm takes two images: a picture and the relative grayscale depth map.
I have also an array of 10 canvases ("layers") initally blank. A note: all the images have the same dimension.
I need to check every pixel X;Y of the depth map and, depending on its color value, access one of the 10 canvases and draw the X;Y pixel of the original image on it.
Resulting algorithm is someting like:
for (var y = 0; y < totalHeight; ++y) {
for (var x = 0; x < totalWidth; ++x) {
var index = (y * totalWidth + x) * 4; // index of the current pixel
// parse depth level using luminosity method
var depthLevel = Math.round(
0.21 * depthData[index] +
0.71 * depthData[index + 1] +
0.07 * depthData[index + 2]
);
// get the proper layer to modify
var layerIndex = Math.floor((layersCount / 256) * depthLevel);
var layerContext = layers[layerIndex].getContext("2d");
var layerData = layerContext.getImageData(0, 0, totalWidth, totalHeight);
layerData.data[index] = originalData[index];
layerData.data[index + 1] = originalData[index + 1];
layerData.data[index + 2] = originalData[index + 2];
layerData.data[index + 3] = originalData[index + 3];
layerContext.putImageData(layerData, 0, 0);
}
A loop like that takes around 3 minutes to complete on a 200x200 image! I'm pretty sure that the slowness is caused by the last function, putImageData. Is there a faster way to draw pixels in the way I need? Thank you
Don't set your image data in every iteration of the loop. That's a heavy operation, and you're executing it 40.000 (200*200) times.
This should save you a bit of processing power:
var contexts = [];
var data = [];
// Save your contexts and data to 2 arrays.
for (var i = 0; i < layers.length; i++) {
contexts[i] = layers[i].getContext("2d");
data[i] = contexts[i].getImageData(0, 0, totalWidth, totalHeight);
}
for (var y = 0; y < totalHeight; ++y) {
for (var x = 0; x < totalWidth; ++x) {
var index = (y * totalWidth + x) * 4; // index of the current pixel
// parse depth level using luminosity method
var depthLevel = Math.round(
0.21 * depthData[index]
+ 0.71 * depthData[index + 1]
+ 0.07 * depthData[index + 2]);
// get the proper layer to modify
var layerIndex = Math.floor((layersCount / 256) * depthLevel);
data[layerIndex].data[index] = originalData[index];
data[layerIndex].data[index + 1] = originalData[index + 1];
data[layerIndex].data[index + 2] = originalData[index + 2];
data[layerIndex].data[index + 3] = originalData[index + 3];
}
}
// Apply your new data to the contexts.
for (var i = 0; i < layers.length; i++) {
contexts[i].putImageData(data[i]);
}
I haven't tested it, but this should give you a bit of an idea of how to do it.