Ny olana Ny ankamaroan'ny algorithms siansa dia miaina ao amin'ny - fanodinana sary sy fanadihadiana, fototra amin'ny fianarana milina, sns. Ny mpikaroka dia manoratra MATLAB, mamoaka lahatsoratra, ary mizara Ny rakitra. Ny Matlab .m Fa ny code famokarana mifototra amin'ireto algorithms dia mihazakazaka any an-kafa. Raha mamorona fampiharana tranonkala izay mila ny kalitaon'ny sary ianao, dia manoratra JavaScript ianao. Noho izany dia mahita taratasy ianao, mamaky ny algorithm, ary hamerina azy indray. Azonao atao ny manoratra ny fitsapana. Jereo fa ny sary mitovy dia miverina amin'ny 1.0, ary ny sary samihafa dia miverina amin'ny lanjany ambany. Fa izany ihany no mahatonga ny bugs mazava. Ny tsara tarehy dia tsy marina ny fandefasana ny sisintany, kely ny coefficients, na tsy misy dingana normalization. Ny code dia mihazakazaka ary miverina ny isa azo antoka, fa tsy manao izay voalaza ao amin'ny taratasy. Ny library JavaScript efa misy dia manana ity olana ity. Izy ireo dia miverina amin'ny port hafa amin'ny fiteny ambany, ary ny taranaka tsirairay dia miala lavitra amin'ny voalohany. Tsy misy olona manamarina ny MATLAB noho ny MATLAB dia miditra vola, ary ny fandefasana azy amin'ny CI dia tsy mahazatra. Nanao izany aho rehefa niara-niasa tamin'ny fitaovana mampitaha sary. Tiako ny fampiharana azo antoka amin'ny SSIM (Structural Similarity Index) sy GMSD (Gradient Magnitude Similarity Deviation). Ny safidy JavaScript dia lava, tsy marina, na ny roa. Idea: mampiasa Octave ho an'ny fanamarinana maimaim-poana, loharanom-baovao malalaka ho an'ny MATLAB. Mihazakazaka amin'ny mitovy ny rakitra, mamokatra ny vokatra mitovy, ary mametraka ao amin'ny server CI. Ny hevitra dia tsotra: Ny GNU Octave dia .m Ampiasao ny fampiharana MATLAB voalohany avy amin'ny taratasy Mandeha izany amin'ny Octave miaraka amin'ny sary fitsapana Mandeha ny fametrahana JavaScript miaraka amin'ny sary mitovy Mifanaraka amin'ny vokatra This isn't unit testing. It's ground-truth validation. You're not checking that your code behaves correctly according to your understanding. You're checking that it matches the reference implementation exactly. Here's what that looks like in practice: function runMatlabGmsd(img1Path: string, img2Path: string): number { const matlabScript = ` addpath('${matlabPath}'); img1 = imread('${img1Path}'); img2 = imread('${img2Path}'); if size(img1, 3) == 3, img1 = rgb2gray(img1); end if size(img2, 3) == 3, img2 = rgb2gray(img2); end result = GMSD(double(img1), double(img2)); fprintf('%.15f', result); `; const output = execSync(`octave --eval "${matlabScript}"`, { encoding: 'utf-8', }); const match = output.match(/(\d+\.\d+)/); return parseFloat(match[0]); } Azonao atao ny mivantana ao amin'ny Octave avy amin'ny Node.js, mamantatra ny valin'ny tsindry, ary mampitaha izany amin'ny vokatry ny JavaScript. Manomboka tsotra: GMSD GMSD measures image similarity using gradients. It computes edge information for both images, compares them pixel-by-pixel, and returns a single score. Lower means more similar. Zero means identical. Ny algorithm dia mifototra amin'ny Izy io dia tsotra, voasoratra tsara, ary tsy manana fametrahana JavaScript efa misy. Xue, W., Zhang, L., Mou, X., & Bovik, A. C. (2013). "Gradient Magnitude Similarity Deviation: A Highly Efficient Perceptual Image Quality Index." The MATLAB Reference Amin'ny ankapobeny dia 35 ny rohy: The original implementation function [score, quality_map] = GMSD(Y1, Y2) % Gradient Magnitude Similarity Deviation % Wufeng Xue, Lei Zhang, Xuanqin Mou, and Alan C. Bovik T = 170; Down_step = 2; dx = [1 0 -1; 1 0 -1; 1 0 -1]/3; dy = dx'; aveKernel = fspecial('average', 2); aveY1 = conv2(Y1, aveKernel, 'same'); aveY2 = conv2(Y2, aveKernel, 'same'); Y1 = aveY1(1:Down_step:end, 1:Down_step:end); Y2 = aveY2(1:Down_step:end, 1:Down_step:end); IxY1 = conv2(Y1, dx, 'same'); IyY1 = conv2(Y1, dy, 'same'); gradientMap1 = sqrt(IxY1.^2 + IyY1.^2); IxY2 = conv2(Y2, dx, 'same'); IyY2 = conv2(Y2, dy, 'same'); gradientMap2 = sqrt(IxY2.^2 + IyY2.^2); quality_map = (2*gradientMap1.*gradientMap2 + T) ./ (gradientMap1.^2 + gradientMap2.^2 + T); score = std2(quality_map); : The algorithm Downsample ny sary roa amin'ny 2x amin'ny alalan'ny solosaina Mifantoka ny habetsaky ny gradient amin'ny operators Prewitt (3x3 edge detection) Hividy ny fahasamihafana isaky ny pixel 4. Hiverina ny fahasamihafana ho an'ny sarin'ny fahasamihafana Ny port JavaScript dia Ny hevitra fototra: ny sary izay toa mitovy dia manana lafiny mitovy. Ny sary sy ny dikan'ny tsindry kely dia manana lafiny ao amin'ny toerana mitovy. Ny sary sy ny sary hafa tanteraka dia tsy. Ny GMSD dia mahazo izany amin'ny alàlan'ny fanombanana ny "gradient magnitude" - amin'ny ankapobeny, ny haavony amin'ny tsirairay amin'ny pixel. Ity dia mampiasa ny Operator Prewitt, filalaovana 3x3 mahazatra izay mahita fiovan'ny haavony sy ny haavony. Ny haavony dia mampifandray ny lalana roa: . sqrt(horizontal² + vertical²) Ao amin'ny MATLAB, ity dia iray amin'ireo Ao amin'ny JavaScript, dia manodidina ny pixel tsirairay sy ny manodidina azy 8 isika: conv2 function computeGradientMagnitudes( luma: Float32Array, width: number, height: number ): Float32Array { const grad = new Float32Array(width * height); for (let y = 1; y < height - 1; y++) { for (let x = 1; x < width - 1; x++) { const idx = y * width + x; // Fetch 3x3 neighborhood const tl = luma[(y - 1) * width + (x - 1)]; const tc = luma[(y - 1) * width + x]; const tr = luma[(y - 1) * width + (x + 1)]; const ml = luma[y * width + (x - 1)]; const mr = luma[y * width + (x + 1)]; const bl = luma[(y + 1) * width + (x - 1)]; const bc = luma[(y + 1) * width + x]; const br = luma[(y + 1) * width + (x + 1)]; // Prewitt Gx = [1 0 -1; 1 0 -1; 1 0 -1]/3 const gx = (tl + ml + bl - tr - mr - br) / 3; // Prewitt Gy = [1 1 1; 0 0 0; -1 -1 -1]/3 const gy = (tl + tc + tr - bl - bc - br) / 3; grad[idx] = Math.sqrt(gx * gx + gy * gy); } } return grad; } Rehefa manana ny habetsahan'ny gradient ho an'ny sary roa ireo ianao, ny GMSD dia mampitaha azy ireo amin'ny pixel-by-pixel ary miverina ny fahasamihafana ho an'ny fahasamihafana. Ny fahasamihafana avo dia midika fa misy toerana samihafa ny lafiny. Ny fahasamihafana ambany dia midika hoe rafitra mitovy. function computeStdDev(values: Float32Array): number { const len = values.length; if (len === 0) return 0; let sum = 0; for (let i = 0; i < len; i++) { sum += values[i]; } const mean = sum / len; let variance = 0; for (let i = 0; i < len; i++) { const diff = values[i] - mean; variance += diff * diff; } variance /= len; return Math.sqrt(variance); } Ny valiny Izany no mahatonga an'i Octave hahazo ny fitazonana azy. Mandeha ny code MATLAB voalohany sy ny fametrahana JavaScript amin'ny sary mitovy ary mampitaha ny vokatra. function runMatlabGmsd(img1Path: string, img2Path: string): number { const matlabPath = join(__dirname, '../matlab'); const matlabScript = [ `addpath('${matlabPath}')`, `img1 = imread('${img1Path}')`, `img2 = imread('${img2Path}')`, `if size(img1, 3) == 3, img1 = rgb2gray(img1); end`, `if size(img2, 3) == 3, img2 = rgb2gray(img2); end`, `result = GMSD(double(img1), double(img2))`, `fprintf('%.15f', result)`, ].join('; '); const output = execSync(`octave --eval "${matlabScript}"`, { encoding: 'utf-8', }); const match = output.match(/(\d+\.\d+)/); return parseFloat(match[0]); } Manafoana izany eto ambany: addpath - milaza an'i Octave ny toerana hitadiavana GMSD.m (ny rakitra MATLAB voalohany avy amin'ny taratasy) imread — mametraka ny sary fitsapana rgb2gray - miova amin'ny grayscale raha ilaina (ny GMSD dia miasa amin'ny famantarana ihany) double() — MATLAB/Octave dia mila fidirana amin'ny lanjany, fa tsy ny uint8 fprintf('%.15f') — vokatra amin'ny toerana dimy ambin'ny folo, haingana ampy mba hahazoana bug subtle Ny regex dia mandray ny isa avy amin'ny vokatra Octave. Octave dia manindry vaovao momba ny fanombohana, noho izany dia mandray ny vokatra amin'ny lanjany fotsiny isika. Amin'ny fampiasana ny Vitest, fa ny Jest na ny rafitra hafa dia miasa toy izany koa: import { execSync } from 'node:child_process'; import { describe, expect, it } from 'vitest'; import { gmsd } from './index'; describe('GMSD - MATLAB Comparison', () => { it('should match MATLAB reference', async () => { const tsResult = gmsd(img1.data, img2.data, undefined, width, height, { downsample: 1, c: 170, }); const matlabResult = runMatlabGmsd(img1Path, img2Path); const percentDiff = Math.abs(tsResult - matlabResult) / matlabResult * 100; console.log(`TypeScript: ${tsResult.toFixed(6)}`); console.log(`MATLAB: ${matlabResult.toFixed(6)}`); console.log(`Diff: ${percentDiff.toFixed(2)}%`); // Accept <2% difference (boundary handling varies) expect(percentDiff).toBeLessThan(2); }); }); Ny fanalahidy dia ny fametrahana ny fahaleovantenan'ny tsara. Tena mafy (0.0001%) ary ianao dia hanenjika ny fanahy amin'ny toerana mihodina. Tena lava (10%) ary ny bugs tena mivoaka. Nandeha tamin'ny 2% ho an'ny GMSD rehefa nahatakatra ny loharanon'ny fahasamihafana (manampahafantarana bebe kokoa momba izany ao amin'ny Results). Ho an'ny CI, Octave dia mametraka ao anatin'ny segondra ao amin'ny Ubuntu: name: Test on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Octave run: sudo apt-get update && sudo apt-get install -y octave - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Install dependencies run: pnpm install - name: Run tests run: pnpm test Amin'izao fotoana izao, ny tsindry tsirairay dia manamarina ny JavaScript amin'ny rohy MATLAB. Raha toa ianao ka tsy nahavita ny fandefasana ny sisintany na manimba ny coefficient, dia tsy mahomby ny fitsapana. Results Image Pair TypeScript MATLAB Difference 1a vs 1b 0.088934 0.089546 0.68% 2a vs 2b 0.142156 0.143921 1.23% 3a vs 3b 0.067823 0.068412 0.86% 1A amin'ny 1B 0.088934 0.089546 0,68 isan-jato Ny 2A dia 2B. 0.142156 0.143921 Ny fitomboan'ny Ny 3A dia 3B. 0.067823 0.068412 Ny 84% Ny 0.68-1.23% fahasamihafana dia avy amin'ny fandefasana sisintany ao amin'ny Ny Matlab dia Amin'ny maha-mpandrosoana ny kalitaon'ny fahatsapana, ity fahasamihafana ity dia azo ekena satria mitovy amin'ny famaritana mifandraika amin'ny sary. conv2 'same' Ampiharina amin'ny: SSIM SSIM (Structural Similarity Index) dia fitsipika ara-barotra ho an'ny fanombanana ny kalitaon'ny sary. Ity dia mampitaha ny fahazavana, ny fifanoherana, ary ny rafitra eo amin'ny sary roa ary miverina ny isa avy amin'ny 0 ka hatramin'ny 1. Ny algorithm dia avy amin'ny Paper. fanombanana ny kalitaon'ny sary: avy amin'ny fahatsapana ny fahadisoana ka hatramin'ny fahasamihafana ara-panorenana. Ity dia voamarina be dia be, fantatra tsara, ary efa manana fampiharana JavaScript. Ny fahasamihafana dia tsy manomboka avy amin'ny fototra isika. Isika dia miditra ao amin'ny efitrano miaraka amin'ny library efa misy, ka afaka mampitaha mivantana ny fahamarinana. Wang, Z., Bovik, A. C., Sheikh, H. R., & Simoncelli, E. P (2004) IEEE Transactions amin'ny fanodinana sary The Existing Landscape Ny fametrahana JavaScript malaza indrindra dia Izany dia miasa. Dia miverina ny isa azo antoka. Fa tsy misy olona nanamarina izany amin'ny MATLAB famantarana. ssim.js Nanao ny fifanarahana aho. Ny vokatra dia mahagaga: Library Difference from MATLAB ssim.js 0.05%-0.73% @blazediff/ssim 0.00%-0.03% Ny JS dia Ny fivoaran'ny fivoaran'ny fivoaran'ny Mifanohitra amin'ny 0.00%-0.03% Izany dia hatramin'ny 24x tsara kokoa. Tsy satria ny ssim.js no voasoratra ratsy - izany no fampiharana ara-drariny. Fa ny fanapahan-kevitra kely dia manangona: kely hafa Gaussian varavarankely, tsy misy auto-downsampling, samihafa fandefasana sisintany. Ny tsirairay dia manampy fahadisoana. The MATLAB Reference Ny fampiharana reference an'i Wang dia sarotra kokoa noho ny GMSD. Ny fanampian'ny fototra dia ny famerenana automatic. Ny sary lehibe dia alaina ambany alohan'ny fampitahana. Izany dia mifanaraka amin'ny fahatsapana ny olombelona (tsy mahita fahasamihafana amin'ny pixel iray amin'ny sary 4K) ary manatsara ny fampisehoana. The core computation: % Automatic downsampling f = max(1, round(min(M,N)/256)); if(f > 1) lpf = ones(f,f); lpf = lpf/sum(lpf(:)); img1 = imfilter(img1, lpf, 'symmetric', 'same'); img2 = imfilter(img2, lpf, 'symmetric', 'same'); img1 = img1(1:f:end, 1:f:end); img2 = img2(1:f:end, 1:f:end); end C1 = (K(1)*L)^2; C2 = (K(2)*L)^2; window = window/sum(sum(window)); mu1 = filter2(window, img1, 'valid'); mu2 = filter2(window, img2, 'valid'); mu1_sq = mu1.*mu1; mu2_sq = mu2.*mu2; mu1_mu2 = mu1.*mu2; sigma1_sq = filter2(window, img1.*img1, 'valid') - mu1_sq; sigma2_sq = filter2(window, img2.*img2, 'valid') - mu2_sq; sigma12 = filter2(window, img1.*img2, 'valid') - mu1_mu2; ssim_map = ((2*mu1_mu2 + C1).*(2*sigma12 + C2)) ./ ... ((mu1_sq + mu2_sq + C1).*(sigma1_sq + sigma2_sq + C2)); mssim = mean2(ssim_map); The algorithm: Downsample if the image is larger than 256px on any side Apply a Gaussian window (11x11, σ=1.5) to compute local statistics Raiso ny habetsaky ny (μ), ny variance (σ2) ary ny covariance ho an'ny varavarankely tsirairay 4 Mifandray amin'ny endrika SSIM miaraka amin'ny fiovaovan'ny C1 sy C2 Hiverina amin'ny habetsaky ny lanjan'ny SSIM rehetra Ny port JavaScript dia Ny ampahany telo dia tsy maintsy mitandrina tsara: ny varavarankely Gausiana, ny convolution azo alaina, ary ny famindrana ny fanodinana. Ny varavarankely Ny Matlab dia dia mamorona kernel 11x11 Gaussian. Ny JavaScript mifanaraka amin'izany: fspecial('gaussian', 11, 1.5) function createGaussianWindow1D(size: number, sigma: number): Float32Array { const window = new Float32Array(size); const center = (size - 1) / 2; const twoSigmaSquared = 2 * sigma * sigma; let sum = 0; for (let i = 0; i < size; i++) { const d = i - center; const value = Math.exp(-(d * d) / twoSigmaSquared); window[i] = value; sum += value; } // Normalize so weights sum to 1 for (let i = 0; i < size; i++) { window[i] /= sum; } return window; } Jereo fa izany dia mamorona varavarankely 1D, fa tsy 2D. Izany dia natao satria ny filalaovan'i Gauss dia azo ampiharina, izay midika fa ny 2D convolution dia azo ampiharina ho roa 1D passes. Ny fifanakalozan-kevitra Ny Matlab dia mampihatra ny kernel 2D. Ho an'ny varavarankely 11x11 amin'ny sary 1000x1000, izany dia 121 tapitrisa multiplications. Separable convolution manapaka izany ho 22 tapitrisa: filter2 function convolveSeparable( input: Float32Array, output: Float32Array, temp: Float32Array, width: number, height: number, kernel1d: Float32Array, kernelSize: number ): void { const pad = Math.floor(kernelSize / 2); // Pass 1: Horizontal convolution (input -> temp) for (let y = 0; y < height; y++) { const rowStart = y * width; for (let x = pad; x < width - pad; x++) { let sum = 0; const srcStart = rowStart + x - pad; for (let k = 0; k < kernelSize; k++) { sum += input[srcStart + k] * kernel1d[k]; } temp[rowStart + x] = sum; } } // Pass 2: Vertical convolution (temp -> output) const outWidth = width - kernelSize + 1; const outHeight = height - kernelSize + 1; let outIdx = 0; for (let y = 0; y < outHeight; y++) { for (let x = 0; x < outWidth; x++) { let sum = 0; const srcX = x + pad; for (let k = 0; k < kernelSize; k++) { sum += temp[(y + k) * width + srcX] * kernel1d[k]; } output[outIdx++] = sum; } } } Ny famerenana automatique This is the part most JavaScript implementations skip. MATLAB's reference downsamples images larger than 256px using a box filter with symmetric padding: function downsampleImages( img1: Float32Array, img2: Float32Array, width: number, height: number, f: number ): { img1: Float32Array; img2: Float32Array; width: number; height: number } { // Create 1D averaging filter const filter1d = new Float32Array(f); const filterValue = 1 / f; for (let i = 0; i < f; i++) { filter1d[i] = filterValue; } // Apply separable filter with symmetric padding const temp = new Float32Array(width * height); const filtered1 = new Float32Array(width * height); const filtered2 = new Float32Array(width * height); convolveSeparableSymmetric(img1, filtered1, temp, width, height, filter1d, f); convolveSeparableSymmetric(img2, filtered2, temp, width, height, filter1d, f); // Subsample by taking every f-th pixel const newWidth = Math.floor(width / f); const newHeight = Math.floor(height / f); const downsampled1 = new Float32Array(newWidth * newHeight); const downsampled2 = new Float32Array(newWidth * newHeight); for (let y = 0; y < newHeight; y++) { for (let x = 0; x < newWidth; x++) { downsampled1[y * newWidth + x] = filtered1[y * f * width + x * f]; downsampled2[y * newWidth + x] = filtered2[y * f * width + x * f]; } } return { img1: downsampled1, img2: downsampled2, width: newWidth, height: newHeight }; } Ny fandehan'io dingana io dia tsy manitsakitsaka na inona na inona mazava. Hahazo ny valin'ny SSIM ianao. Fa tsy mifanaraka amin'ny fanehoan-kevitra izy ireo, ary ny algorithm dia mihazakazaka amin'ny sary lehibe. Validation Ny endrika mitovy amin'ny GMSD. Miantso Octave, mampitaha ny vokatra: function runMatlabSsim(img1Path: string, img2Path: string): number { const matlabPath = join(__dirname, '../matlab'); const matlabScript = [ `addpath('${matlabPath}')`, `img1 = imread('${img1Path}')`, `img2 = imread('${img2Path}')`, `if size(img1, 3) == 3, img1 = rgb2gray(img1); end`, `if size(img2, 3) == 3, img2 = rgb2gray(img2); end`, `result = ssim(double(img1), double(img2))`, `fprintf('%.15f', result)`, ].join('; '); const output = execSync(`octave --eval "${matlabScript}"`, { encoding: 'utf-8', }); const match = output.match(/(\d+\.\d+)/); return parseFloat(match[0]); } Ny fahazoan-dalana dia matanjaka eto. SSIM dia algorithm tsara voafaritra amin'ny coefficients marina. Raha mihoatra noho ny 0,05% isika dia misy zavatra diso: it('should match MATLAB for images 1a vs 1b', async () => { const tsResult = ssim(png1.data, png2.data, undefined, width, height); const matlabResult = runMatlabSsim(img1Path, img2Path); const percentDiff = Math.abs(tsResult - matlabResult) / matlabResult * 100; // Strict: must be within 0.05% expect(percentDiff).toBeLessThan(0.05); }); Ny vokatra Image Pair @blazediff/ssim MATLAB Difference 1a vs 1b 0.968753 0.968755 0.00% 2a vs 2b 0.912847 0.912872 0.00% 3a vs 3b 0.847621 0.847874 0.03% 1A amin'ny 1B 0.968753 0.968755 Ny 0.00% 2a vs 2b 0.912847 0.912872 0.00% Ny 3A dia 3B. 0.847621 0.847874 0.03% Raha oharina amin'ny ssim.js amin'ny sary mitovy: Image Pair ssim.js MATLAB Difference 1a vs 1b 0.969241 0.968755 0.05% 2a vs 2b 0.916523 0.912872 0.40% 3a vs 3b 0.853842 0.847874 0.73% 1a vs 1b 0.969241 0.968755 0.05% Ny 2A dia 2B. 0.916523 0.912872 0.40% 3a vs 3b 0.853842 0.847874 0.73% Ny 0,00-0,03% fahasamihafana eo amin'ny fampiharana dia vokatry ny fivoaran'ny tsindry. Tsy azo esorina amin'ny fampiharana amin'ny fiteny. Ny vokatra dia mitovy amin'ny MATLAB. Ny fahasamihafana amin'ny ssim.js dia avy amin'ny safidy algorithmic: tsy misy ny famerenana ny tsindry, kely hafa Gaussian famaritana, ary samihafa ny fandefasana ny sisintany. Common Pitfalls Ny fidirana amin'ny MATLAB amin'ny JavaScript dia toa tsotra mandra-pahatongan'izany. Indrindra ny Indexing MATLAB arrays start at 1. JavaScript arrays start at 0. Everyone knows this. Everyone still gets bitten by it. The bug is rarely obvious. You won't get an off-by-one error on the first pixel. You'll get slightly wrong values at image boundaries, where loops like Mifeno amin'ny Ny fidirana ao amin'ny manodidina becomes , izay mandresy amin'ny index -1 rehefa Ny 0. for i = 1:height for (let i = 0; i < height; i++) img(i-1, j) img[(i-1) * width + j] i Fix: mamorona ohatra 3x3 eo amin'ny taratasy alohan'ny hanoratra ny taratasy. Column-Major vs Row-Major MATLAB dia mitahiry matrices column-by-column. JavaScript TypedArrays dia mitahiry row-by-row. A 3x3 matrix in MATLAB: [1 4 7] [2 5 8] → stored as [1, 2, 3, 4, 5, 6, 7, 8, 9] [3 6 9] The same matrix in JavaScript: [1 4 7] [2 5 8] → stored as [1, 4, 7, 2, 5, 8, 3, 6, 9] [3 6 9] This matters when you translate indexing. MATLAB's Ho lasa in JavaScript, not . img(row, col) img[row * width + col] img[col * height + row] Ny ankamaroan'ny tranonkala sary dia efa manome anao ny angon-drakitra lehibe, noho izany dia tsara ianao. Fa raha mamoaka ny matrix MATLAB operations verbatim ianao, jereo. Fandrahonana ara-batana Ny Matlab dia , Ary have different default behaviors: conv2 filter2 imfilter conv2(A, K) — tsy misy fanapahana, ny vokatra dia mihena — zero padding, output same size as input conv2(A, K, 'same') Filter2(K, A, 'valid') — tsy misy fanapahana, ny fivoarana dia mihena — mirror padding at edges imfilter(A, K, 'symmetric') Get this wrong, and your results will differ at every pixel near the border. For a 1000x1000 image with an 11x11 kernel, that's ~4% of your pixels. Ny fampiasana ny SSIM reference Ny Ny fanodinana ho an'ny downsampling, dia Ny mode for the main computation. Miss either detail, and you'll wonder why your numbers are 2% off. imfilter 'symmetric' filter2 'valid' Color Space Coefficient amin'ny loko Ny famerenana ny RGB ho an'ny grayscale dia toa tsotra. Ampitombo amin'ny coefficients, ampitombo ny fantsona. Fa inona ny coefficients? Ny Matlab dia uses BT.601: rgb2gray Y = 0.298936 * R + 0.587043 * G + 0.114021 * B Ny lisitry ny JavaScript sasany dia mampiasa ny BT.709: Y = 0.2126 * R + 0.7152 * G + 0.0722 * B Ny sasany dia mampiasa ny tsingerintaona tsotra: Y = (R + G + B) / 3 The difference is negligible for most images. But if you're validating against MATLAB, use the exact BT.601 coefficients. Otherwise, you'll chase phantom bugs that are really just a grayscale conversion mismatch. MATLAB’s Implicit Behaviours MATLAB does things automatically that JavaScript won't: : MATLAB's converts uint8 (0-255) to float (0.0-255.0). If your JavaScript reads PNG data as Uint8ClampedArray and you forget to convert, your variance calculations will overflow. Auto-casting double(img) : MATLAB functions have default values buried in their documentation. SSIM uses , , varavarankely lehibe 11, sigma 1.5. misalasala iray, ary ny fampiharana dia miova. Default parameters K = [0.01, 0.03] L = 255 Optimizing Your JavaScript Ny fahamarinana no voalohany. Fa rehefa mifanaraka amin'ny MATLAB ny fampiharana, ny fahombiazana dia zava-dehibe. Ny algorithm momba ny siansa dia mandrindra tapitrisa pixel. These optimizations made my SSIM implementation 25-70% faster than ssim.js while maintaining accuracy. Use TypedArrays Regular JavaScript arrays are flexible. They can hold mixed types, grow dynamically, and have convenient methods. That flexibility costs performance. // Slow: regular array const pixels = []; for (let i = 0; i < width * height; i++) { pixels.push(image[i] * 0.299); } // Fast: TypedArray const pixels = new Float32Array(width * height); for (let i = 0; i < width * height; i++) { pixels[i] = image[i] * 0.299; } TypedArrays have fixed size, fixed type, and contiguous memory. The JavaScript engine knows precisely how to optimize them. For numerical code, this is a 2-5x speedup. Use for most computations. Use when you need extra precision (accumulating sums over large images). Use Ho an'ny famokarana sary farany. Float32Array Float64Array Uint8ClampedArray Filter tsy miankina Ny convolution 2D miaraka amin'ny kernel NxN dia mitaky famerenana N2 isaky ny pixel. Ho an'ny SSIM 11x11 Gaussian, izany dia 121 asa isaky ny pixel. But Gaussian filters are separable. A 2D Gaussian is the outer product of two 1D Gaussians. Instead of one 11x11 pass, you do two 11x1 passes: 22 operations per pixel. // Slow: 2D convolution (N² operations per pixel) for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { let sum = 0; for (let ky = 0; ky < kernelSize; ky++) { for (let kx = 0; kx < kernelSize; kx++) { sum += getPixel(x + kx, y + ky) * kernel2d[ky][kx]; } } output[y * width + x] = sum; } } // Fast: separable convolution (2N operations per pixel) // Pass 1: horizontal for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { let sum = 0; for (let k = 0; k < kernelSize; k++) { sum += getPixel(x + k, y) * kernel1d[k]; } temp[y * width + x] = sum; } } // Pass 2: vertical for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { let sum = 0; for (let k = 0; k < kernelSize; k++) { sum += temp[(y + k) * width + x] * kernel1d[k]; } output[y * width + x] = sum; } } Izany dia miasa amin'ny kernel tsirairay: Gaussian, box filter, na Sobel. Jereo raha ny kernel dia tsirairay alohan'ny hanatanterahana 2D convolution. Miverina amin'ny buffers Ny fametrahana ny fahatsiarovana dia lafo. Ny fanangonana ny fako dia ratsy kokoa. Fametrahana indray mandeha, mampiasa indray any amin'ny toerana rehetra. // Slow: allocate per operation function computeVariance(img: Float32Array): Float32Array { const squared = new Float32Array(img.length); // allocation const filtered = new Float32Array(img.length); // allocation // ... } // Fast: pre-allocate and reuse class SSIMComputer { private temp: Float32Array; private squared: Float32Array; constructor(maxSize: number) { this.temp = new Float32Array(maxSize); this.squared = new Float32Array(maxSize); } compute(img: Float32Array): number { // reuse this.temp and this.squared } } Ho an'ny SSIM, dia manome ny buffers rehetra mialoha aho: sary mainty, sary efitra, outputs voasoratra, ary ny sarin'ny SSIM. Cache Computed Values Some values get reused. Don’t compute them twice. // Slow: recompute Gaussian window every call function ssim(img1, img2, width, height) { const window = createGaussianWindow(11, 1.5); // expensive // ... } // Fast: cache by parameters const windowCache = new Map<string, Float32Array>(); function getGaussianWindow(size: number, sigma: number): Float32Array { const key = `${size}_${sigma}`; let window = windowCache.get(key); if (!window) { window = createGaussianWindow(size, sigma); windowCache.set(key, window); } return window; } Ny varavarankely Gausiana ho an'ny SSIM dia 11x11 amin'ny σ = 1.5. Ny fandinihana dia mitaky mikrosekonda. Aza miala amin'ny fanaraha-maso ny sisin-dranomasina ao amin'ny hot loops JavaScript arrays check bounds on every access. For interior pixels where you know indices are valid, this is wasted work. // Slow: bounds checking on every access for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const left = x > 0 ? img[y * width + x - 1] : 0; const right = x < width - 1 ? img[y * width + x + 1] : 0; // ... } } // Fast: handle borders separately // Interior pixels (no bounds checking needed) for (let y = 1; y < height - 1; y++) { for (let x = 1; x < width - 1; x++) { const left = img[y * width + x - 1]; // always valid const right = img[y * width + x + 1]; // always valid // ... } } // Handle border pixels separately with bounds checking Fa ho an'ny sary 1000x1000, dia manala an'arivony ny fanaraha-maso voafetra ianao. Results Benchmarked amin'ny sary mitovy, ny fitaovana mitovy: Implementation Time (avg) vs MATLAB accuracy ssim.js 86ms 0.05-0.73% @blazediff/ssim 64ms 0.00-0.03% ssim.js 86ms 0.05-0.73% @blazediff/ssim 64 taona 0.00-0.03% 25% haingana kokoa sy marina kokoa. Tsy noho ny algorithms malaza - noho ny fikarakarana tsara. TypedArrays, azo ampiharina filalaovana, buffer reuse, cached varavarankely. For the varianta (mampiasa sary integral fa tsy convolution), ny fahasamihafana dia lehibe kokoa: 70% haingana kokoa noho ny ssim.js amin'ny sary lehibe. Hitchhiker's SSIM Takeaways Ny fametrahana algorithms siansa avy amin'ny MATLAB amin'ny JavaScript dia mekanika rehefa manana ny dingana tsara ianao. Ny taratasy dia mamaritra ny algorithm. Ny code MATLAB dia mampihatra azy ireo. Tsy mitovy amin'izany. Case Edge, parameters default, border handling - izy ireo ao amin'ny code, fa tsy ny taratasy. Manomboka amin'ny file. Use the reference implementation .m Izany dia maimaim-poana, mihazakazaka any amin'ny toerana rehetra, ary mihazakazaka ny MATLAB code tsy misy fanitsiana. Mametraka fitsapana fototra alohan'ny. Mihazakazaka azy ireo ao amin'ny CI. Rehefa mifanaraka amin'ny isa 4 desimal toerana, dia vita ianao. Rehefa tsy, fantatrao avy hatrany. Validate with Octave Aoka ny fampiharana mifanaraka amin'ny MATLAB alohan'ny hanatsarana. Ny valiny ratsy haingana dia mbola diso. Rehefa marina ianao, ny fanatsarana dia tsotra: TypedArrays, filalaovana azo alaina, fampiasana indray ny buffer. Accuracy first, then performance Indraindray dia tsy mifanaraka amin'ny marina ianao. Ny fandefasana ny sisintany, ny fanamafisana amin'ny lanjany, ary ny fanamafisana amin'ny tanjona. Izany no tsara. Fantaro ny antony tsy mitovy sy ny habetsaky ny habetsaky ny habetsaky ny lanjany. Document your differences Ny fahasamihafana eo amin'ny MATLAB akademika sy ny famokarana JavaScript dia kely kokoa noho ny hita. Ny algorithms dia mitovy. Ny matematika dia mitovy. Mila fomba hahazoana antoka fa nahazo izany tsara ianao. Ny fampiharana SSIM sy GMSD avy amin'ity lahatsoratra ity dia azo jerena ao amin'ny [blazediff.dev] (https://blazediff.dev). MIT fahazoan-dalana, tsy misy miankina, voamarina amin'ny MATLAB.