Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need tool to score output on psychovisual equivalency versus other renderers. #66

Open
ILOVEPIE opened this issue Oct 18, 2022 · 7 comments
Labels
enhancement New feature or request hacktoberfest Issues that are good for hacktoberfest
Milestone

Comments

@ILOVEPIE
Copy link
Member

ILOVEPIE commented Oct 18, 2022

This should use a still image perceptual comparison algorithm to compare output. Something similar to PSNR.

@ILOVEPIE ILOVEPIE added enhancement New feature or request hacktoberfest Issues that are good for hacktoberfest labels Oct 18, 2022
@ILOVEPIE ILOVEPIE added this to the First Release milestone Oct 18, 2022
@abhishekmg
Copy link

@ILOVEPIE , please do check out this function in this JS playground https://playcode.io/1752106

if the RGB values match then we get a value as Infinity

Please do let me know if this is what you are looking for, if not any inputs on what exactly needed is much appreciated !

If this is what you are looking for I would be much interested in raising a PR !

@ILOVEPIE
Copy link
Member Author

ILOVEPIE commented Feb 7, 2024

Well, you want to do PSNR or SSIM on XYZ colors, not RGB, also your code isn't taking the Mean Squared Error correctly.

This is more what I'm talking about:

function multiplyMatrixByVector(matrix, vector) {
    let result = [0, 0, 0];
    for (let i = 0; i < 3; i++) {
        for (let j = 0; j < 3; j++) {
            result[i] += matrix[i * 3 + j] * vector[j];
        }
    }
    
    return result;
}

function sRGBtoRGB(sR,sG,sB){
	const color = [sR,sG,sB];
  for(let i = 0; i < 3; i++){
  	if(color[i] <= 0.04045){
        color[i] /= 12.92;
    }else{
        color[i] = Math.pow((color[i]+0.055)/1.055,2.4);
    }
  }
  return color;
}
const DP3toLinearDP3 = sRGBtoRGB;

function sRGBtoXYZ(sR,sG,sB){
	const RGBtoXYZMatrix = [0.4360413,0.3851129,0.1430458,0.2224845,0.7169051,0.0606104,0.0139202,0.0970672,0.7139126];
	const RGB = sRGBtoRGB(sR,sG,sB);
  return multiplyMatrixByVector(RGBtoXYZMatrix,RGB);
}

function DP3toXYZ(wR,wG,wB){
	const LinearDP3toXYZMatrix = [0.5151187,0.2919778,0.1571035,0.2411892,0.6922441,0.0665668,-0.0010505,0.0418791,0.7840713];
	const LinearDP3 = DP3toLinearDP3(wR,wG,wB)
  return multiplyMatrixByVector(LinearDP3toXYZMatrix,LinearDP3)
}

function imageDataToXYZ(d){
	const data = d.data;
  const colorSpace = d.colorSpace;
  const length = d.width*d.height*3;
  const output = new Array(length);
  for(let i = 0; i < length; i+=3){
		let XYZ;
    switch(colorSpace){
    	default:
				console.warn("Unrecognized color space '"+colorSpace+"' assuming srgb, colors may be wrong.");
			case "srgb":
      	XYZ = sRGBtoXYZ(data[0]/255,data[1]/255,data[2]/255);
        break;
      case "display-p3":
				XYZ = DP3toXYZ(data[0]/255,data[1]/255,data[2]/255);
        break;
		}
    output[i] = XYZ[0];
    output[i+1] = XYZ[1];
    output[i+2] = XYZ[2];
	}
  return output;
}

function diffArray(a,b){
	const maxLength = Math.max(a.length, b.length);
  const output = new Array(maxLength);
  for(let i = 0; i < maxLength; i++){
		output[i] = (a[i]||0)-(b[i]||0);
  }
  return output;
}

function selectArrayFromArray(a,n,x){
	const offset = typeof(x) === "number" ? x : 0;
	const length = Math.trunc(a.length/n);
  const output = new Array(length);
  for(let i = 0; i < length; i++){
		output[i] = a[(i*n)+offset];
  }
  return output;
}

function psnr(image1,image2) {
		const image1xyz = imageDataToXYZ(image1);
    const image2xyz = imageDataToXYZ(image2);
    
    let mse;
   	{
    	const xyzDiffSquared = diffArray(image1xyz,image2xyz).map((value) => Math.pow(value,2));
      const mseComponentLength = Math.trunc(xyzDiffSquared.length/3);
      // Calculate the mean squared error for each channel
      const mseX = selectArrayFromArray(xyzDiffSquared,3).reduce((sum,value) => sum+value,0)/mseComponentLength;
      const mseY = selectArrayFromArray(xyzDiffSquared,3,1).reduce((sum,value) => sum+value,0)/mseComponentLength;
      const mseZ = selectArrayFromArray(xyzDiffSquared,3,2).reduce((sum,value) => sum+value,0)/mseComponentLength;
      // Calculate the mean squared error across all channels
      mse = (mseX + mseY + mseZ) / 3;
    }

    // Maximum possible pixel value (we are using floating point values)
    const maxPixelValue = 1.0;

		// Return PSNR value.
    return 20 * Math.log10(maxPixelValue / Math.sqrt(mse));
}

@abhishekmg
Copy link

abhishekmg commented Feb 7, 2024

Right Thank you for the input ! Things look a bit complex to me 😆 still need to look into this to understand it better

But it looks like this is the function you are looking for, so this issue can be closed ?

@ILOVEPIE
Copy link
Member Author

ILOVEPIE commented Feb 7, 2024

But it looks like this is the function you are looking for, so this issue can be closed ?

Not exactly, I was looking for someone to integrate this into the unit test suite.

@abhishekmg
Copy link

Like having the function that you mentioned here #66 (comment) as a helper function in src/__tests__/test-utils folder ?

@ILOVEPIE
Copy link
Member Author

ILOVEPIE commented Feb 8, 2024 via email

@ILOVEPIE ILOVEPIE moved this to In progress in SABRE.js Feb 19, 2024
@ILOVEPIE
Copy link
Member Author

I've coded up a working implementation that does PSNR and SSIM, but we need to convert it from a webpage to a node tool that can be used to compare with output from libass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request hacktoberfest Issues that are good for hacktoberfest
Projects
Status: In progress
Development

No branches or pull requests

2 participants