Problemlər Bilim algoritmalarının əksəriyyəti - Görüntüləri işləmək və analiz etmək, makine öyrənmə prinsipləri, etc. Araşdırmacılar MATLAB yazmaq, məlumat yaymaq və paylaşmaq Filtrlər MATLAB haqqında .m Amma bu algoritmalara dayanan istehsal kodu başqa yerdə çalışır. Görüntü kalitəsi ölçütlərinə ehtiyacı olan bir web app yaratırsanız, JavaScript yazırsınız. Belə ki, bir kağızı bulursunuz, algoritmamı oxuyursunuz və onu reimplement edin. Xatırlada bilərik ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir. VVD - Hollandiyada futbolçu bu adla tanımır, orada VVD daha çox mərkəz-sağı təmsilən edən siyasi partiyanın adının qısaltması kimi bilinir - artıq sorğu-suala ehtiyacı olmayan ulduzdu. Mən bu işdə çalışdıqdan sonra bu işə girdim. SSIM (Structural Similarity Index) və GMSD (Gradient Magnitude Similarity Deviation) istifadə etmək istəyirdim. JavaScript seçkiləri ya yavaş, ya da yanlış idi, ya da hər ikisi. OXŞAR XƏBƏRLƏR: Verifikasiya üçün Octave Bu, MATLAB-a özəlləşdirilmiş, özəlləşdirilmiş bir alternativdir. Əsas səhifə » Xəbərlər » Əsas səhifə » Əsas səhifə » Əsas səhifə GNU Octave proqramı .m Orijinal MATLAB implementasiyasını kağızdan alın Xatırladaq ki, “Octave”də test görüntüləri təqdim olunacaq. “JavaScript” versiyasını təkrarlayın SonrakıSonrakı Sonuçları Bu birim test deyil. Bu həqiqət validasiyasıdır. Kodunuzun sizin anlayışınızla düzgün davranmadığını kontrol etmirsiniz. Reference implementi ilə tam başa çatdığını kontrol edirsiniz. 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]); } Siz Node.js-dən Octave-i çağırırsınız, floating-point nəticəsini analiz edər və onu JavaScript-in çıxışı ilə birləşdirirsiniz. Starting Simple: GMSD GMSD görüntü bənzərliyini gradientlər ilə ölçür. Hər iki görüntü üçün edge xəbərini hesabatlandırır, onları pixel-by-pixel-ə bənzəyir və tek bir qiymət qaytarır. Bu algoritma bazardır Bu, açıqdır, yaxşı dokumentlaşdırılmışdır və var olan JavaScript implementasiyası yoxdur. Xue, W., Zhang, L., Mou, X., & Bovik, A.C. (2013). "Gradient Magnitude Similarity Deviation: A Highly Efficient Perceptual Image Quality Index" IEEE Transactions on Image Processing. MATLAB referensləri 35 dəqiqə: Əvvəlki implementasiya 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 both images by 2x using an averaging filter Prewitt operatorları ilə gradiyent ölçülərini hesablayın (3x3 edge detektasiya) Xatırladaq ki, bu, bir neçə ildir ki, ABŞ-ın “Turkish Airlines” şirkəti “Turkish Airlines” şirkəti ilə əlaqə saxlayır. JavaScript portları Bir foto və onun bir az qısa sürətlənmiş versiyası da aynı yerlərdə qapıları vardır. bir foto və tamamilə ayrı foto yoxdur. GMSD bunu "gradient ölçüsü" hesablayaraq çəkir - mənbəyi olaraq, hər bir pikselin qapılarının nə qədər güclü olduğunu. Bu, horizontal və vertikal ölçüyü dəyişdirən klasik 3x3 filtr olan Prewitt operatorunu istifadə edir. . sqrt(horizontal² + vertical²) MATLAB-da, bu bir-lineer . In JavaScript, we loop through each pixel and its 8 neighbors: 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; } Once you have gradient magnitudes for both images, GMSD compares them pixel-by-pixel and returns the standard deviation of the differences. High deviation means the edges are in different places. Low deviation means a similar structure. 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); } Validasiya Biz orijinal MATLAB kodunu və bizim JavaScript implementasiyamızı aynı görüntülərdə sürətləndiririk və çıxartmalarını birləşdiririk. 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]); } Bunu aşağı bölüşmək: Addpath — GMSD.m (dokumentdən orijinal MATLAB dosyası) buludunu söyləyir Təsadüfi – test görüntüləri yükləyir Rgb2gray — ehtiyac varsa grişə dönüşür (GMSD yalnız luminansda işləyir) Double() — MATLAB/Octave uint8 deyil, floating-point girişinə ehtiyac var fprintf('%.15f') — 15 decimal yerə çıxarılacaq, düz bugləri tapmaq üçün kifayət qədər həddi Xatırladaq ki, “Octave” “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave” şirkəti “Octave”. VVD - Hollandiyada futbolçu bu adla tanımır, orada VVD daha çox mərkəz-sağı təmsilən edən siyasi partiyanın adının qısaltması kimi bilinir. 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); }); }); The key is setting the right tolerance. Too strict (0.0001%) and you'll chase floating-point ghosts. Too loose (10%) and real bugs slip through. I landed on 2% for GMSD after understanding the sources of the differences (more on that in Results). For CI, Octave installs in seconds on 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 Artıq hər bir pulsuz JavaScript-inizi MATLAB referensinə qarşı valideyər.Əgər yanlışlıqla bir limit işlətmək və ya bir koeficient üzə çıxırsanız, test başarısız olur. Sonrakı 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 və 1b 0.088934 0.089546 0 0 68 % 2a və 2b 0.142156 0.143921 1.23% 3a vs 3b 0.067823 0.068412 Xüsusilə 0.86% The 0.68-1.23% difference comes from boundary handling in Matlab xəritədə Movqe.Az xəbər verir ki, Hollandiyada, erkən parlament seçkilərin nəticəsi çox az fərqlə iqtidardakı Liberal Partiyasının (VVD) qazandığını göstərir. conv2 'same' Təsəvvür etmək: SSIM SSIM (Structural Similarity Index) iki görüntü arasında aydınlıq, kontrast və strukturu bir-birinə bənzəyir və 0 ilə 1 arasında bir qiymət qaytarır. The algorithm comes from Xatırladaq ki, bu, bir neçə ildir ki, bir neçə ildir ki, bu, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir il deyil. Wang, Z., Bovik, A. C., Sheikh, H. R., & Simoncelli, E. P (2004) IEEE Transactions on Image Processing Əvvəllərki landscape Ən çox istifadə olunan JavaScript Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə ssim.js İndi baxıram ki, nəticələr şaşırtıcı idi: Library Difference from MATLAB ssim.js 0.05%-0.73% @blazediff/ssim 0.00%-0.03% Əsas səhifə.js Tədbirlər 0.05% 0.73% Xatırladaq ki, Tədbirlərin sayı 0,03% Bu, 24 dəfə daha həqiqətlidir. ssim.js-in pis yazılması olmadığı üçün deyil – bu, həqiqətli bir təsvirdir.Ama küçük qərarlar toplanır: bir az fərqli Gaussian qapıları, auto-downsampling, fərqli limiti işləmək kifayət deyil. MATLAB referensləri Wang'ın referans implementasiyası GMSD-dən daha kompleksdir. Ötən hissə automatik downsamplingdir.Böyük görüntülər əvəz etmədən öncə ölçülür.Bu, insan perceptiyasına uyğunlaşır (bir 4K görüntülərdə tek piksel fərqləri görmürük) və performansını yaxşılaşdırır. İncəsənət kompüterləri : % 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: Görüntülər 256 px-dən çoxsa Yerli statistikaları hesabatlandırmaq üçün Gausiya pencerəsi (11x11, σ=1.5) istifadə edin Hər bir qapı üçün ortalama (μ), varians (σ2) və kovarians hesablayın Combine into the SSIM formula with stability constants C1 and C2 Return the mean of all local SSIM values JavaScript portları Üç hissəyə dikkatli baxmaq lazımdır: Gausiya qapıları, ayrılabilir konvolisyon və downsampling filtri. Gausyanın pencerəsi Matlab xəritədə 11x11 Gaussian kernel yaratır. 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; } Notice that this creates a 1D window, not a 2D one. That's intentional because Gaussian filters are separable, meaning a 2D convolution can be split into two 1D passes. Same result, half the operations. Ayrılıq konvulsiyası Matlab xəritədə Xatırladaq ki, bu, “Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq: 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; } } } Automatik downsampling MATLAB-in referensiyası simetrik padding ilə bir kutu filtrəsi ilə 256 px-dən böyük görüntüləri aşağı salır: 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 }; } Bu adımı keçmək açıq bir şey qırmaz.Sadece SSIM qiymətləri alırsınız.Ancaq reference ilə qarşılaşmayacaqlar və algoritma böyük görüntülərdə daha yavaş çalışacaq. Validasiya Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırladaq ki, Xatırlama: 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]); } Burada tolerans daha da şiddətlidir. SSIM tam koeficientlər ilə yaxşı tanımlanmış bir algoritmadır. Əgər 0.05%-dən çoxsa, bir şey yanlışdır: 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); }); Sonrakı 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 və 1b 0.968753 0.968755 0,00 faiz 2a və 2b 0.912847 0.912872 0,00 faiz 3a vs 3b 0.847621 0.847874 0.03% Aynı görüntülərdə ssim.js ilə əvəzlənir: 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 Xüsusilə 0.05% 2a və 2b 0.916523 0.912872 0.40% 3a və 3b 0.853842 0.847874 Əməkdaşlıq 0,73% Mənim implementasiyamda 0,00-0,03%-lik bir fərq var ki, bu, dillər arasındakı çevirmədə önəmlidir. VVD - Hollandiyada futbolçu bu adla tanımır, orada VVD daha çox mərkəz-sağı təmsil edən siyasi partiyanın adının qısaltması kimi bilinir - artıq sorğu-suala ehtiyacı olmayan ulduzdu. Ordinary pedaqoji VVD - Hollandiyada futbolçu bu adla tanımır, orada VVD daha çox mərkəz-sağı təmsilən edən siyasi partiyanın adının qısaltması kimi bilinir - artıq sorğu-suala ehtiyacı olmayan ulduzdu. Array Indexing MATLAB arrays start at 1. JavaScript arrays start at 0. Everyone knows this. Everyone still gets bitten by it. Xatırladaq ki, bir neçə ildir ki, bu problemin başlanğıcına səbəb ola bilməz, amma bir neçə ildir ki, bu problemin başlanğıcına səbəb olur. Tədbirlər but the neighbor access Gələcək , which hits index -1 when is 0. for i = 1:height for (let i = 0; i < height; i++) img(i-1, j) img[(i-1) * width + j] i Xatırladaq ki, bir neçə il öncə “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”nin “Qəbələ”in “Qəbələ”in “Qəbələ”in “Qəbələ”in “Qəbələ”in “Qəbələ”in “Qəbələ”in “Qəbələ”in “Qəbələ”in “Qəbələ”in”in “Qəbələ”in “Qəbələ Column-Major vs Row-Major Xatırladaq ki, ABŞ-ın İŞİD-in hökmranlığı ilə bağlı siyasi partiyaların hökmranlığı ilə bağlı problemlər yaranıb. 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] JavaScript-də belə bir matrix var: [1 4 7] [2 5 8] → stored as [1, 4, 7, 2, 5, 8, 3, 6, 9] [3 6 9] İndi bu problemlər həll olunacaq. - MATLAB becomes “JavaScript” və ya . img(row, col) img[row * width + col] img[col * height + row] Xatırladaq ki, çoxlu sayda fotoşəkil aparılır, lakin çoxlu sayda fotoşəkil aparılır, lakin çoxlu sayda fotoşəkil aparılır. Boundary Handling MATLAB's və , and Düzgün davranışları var: conv2 filter2 imfilter — no padding, output shrinks conv2(A, K) — zero padding, output same size as input conv2(A, K, 'same') — no padding, output shrinks filter2(K, A, 'valid') — mirror padding at edges imfilter(A, K, 'symmetric') Bunu yanlışlaşdırın və nəticələriniz hər bir pikselin yaxınlığında fərqlənəcək.11x11 kernel ilə 1000x1000 görüntü üçün bu, pikselinizin ~4%-dir. The SSIM reference uses with Düşünürəm ki, o zaman with mode for the main computation. Miss either detail, and you'll wonder why your numbers are 2% off. imfilter 'symmetric' filter2 'valid' Space Coefficient xəritədə Xatırladaq ki, RGB-nin dəyişdirilməsi çox asandır. Matlab xəritədə 601-ci il tarixində rgb2gray Y = 0.298936 * R + 0.587043 * G + 0.114021 * B Bazı JavaScript kütləvi BT.709 istifadə edir: Y = 0.2126 * R + 0.7152 * G + 0.0722 * B Others use the simple average: 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-in əsas xüsusiyyətləri MATLAB JavaScript-in etmədiyi şeyləri otomatik olaraq edir: : MATLAB's Xatırladaq ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir. Auto-casting double(img) MATLAB funksiyaları dokumentasiyalarında varsayılan qiymətlərdir. və “Sigma 1.5”, “Sigma 11” və “Sigma 1.5”, “Sigma 1” və “Sigma 1” ilə işləyir. Default parameters K = [0.01, 0.03] L = 255 JavaScriptinizin optimalizasiyası Əvvəlki məqaləİnteraktiv.az-a istinadən xəbər verir ki, ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın ABŞ-ın Bu optimizasyonlar SSIM implementasiyamı ssim.js-dən 25-70% daha sürətli etdi. 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. Kullanım for most computations. Use when you need extra precision (accumulating sums over large images). Use Final görüntüləri üçün. Float32Array Float64Array Uint8ClampedArray Ayrı filtre Bir NxN kernel ilə 2D konvulsiyası pixel başına N2 multiplikasiya lazımdır. SSIM 11x11 Gaussian üçün bu, pixel başına 121 operasiyadır. 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; } } VVD - Hollandiyada futbolçu bu adla tanımır, orada VVD daha çox mərkəz-sağı təmsilən edən siyasi partiyanın adının qısaltması kimi bilinir - artıq sorğu-suala ehtiyacı olmayan ulduzdu. Yenilənmiş buffers Memory allocation is expensive. Garbage collection is worse. Allocate once, reuse everywhere. // 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 } } For SSIM, I allocate all buffers upfront: grayscale images, squared images, filtered outputs, and the SSIM map. One allocation at the start, zero during computation. Hesab olunan qiymətlər 2 dəfə oxumaq lazım deyil, 2 dəfə oxumaq lazımdır. // 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; } SSIM üçün Gaussian pencerəsi hər zaman σ=1.5 ilə 11x11-dir. bilmək mikrosekundlar alır. Sürücülər üçün Sürücülər üçün Sürücülər üçün Sürücülər Xatırladaq ki, hər bir quraşdırma aparıcısının quraşdırılması ilə bağlı problemlər həll olunacaq. // 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 Bu, daha çirkin koddur, lakin 1000x1000 görüntülərə görə, milyonlarca kondisiyalı check-in istifadə edirsiniz. Sonrakı Benchmarked on the same images, same machine: Implementation Time (avg) vs MATLAB accuracy ssim.js 86ms 0.05-0.73% @blazediff/ssim 64ms 0.00-0.03% Əsas səhifə.js 86ms 0.05-0.73% Xatırladaq ki, 64 dəqiqə Xüsusilə 0,03% 25 % daha sürətli və daha hədsizdir. Zərif algoritmalarla deyil – mükəmməl inqilabdan ötürülür.TypedArrays, ayrılabilir filtrlər, buffer reuse, cached windows. Onun üçün variant (uses integral images instead of convolution), the gap is wider: 70% faster than ssim.js on large images. Hitchhiker-in şagirdləri Takeaways MATLAB-dən JavaScript-ə bilim algoritmalarının portlaşdırılması düzgün prosesi olduğunda mekanikdir. Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə » Əsas səhifə Filtrə Use the reference implementation .m O, hər yerdə çalışır və modifikasiya olmadan MATLAB kodunu işləyir. Əvvəlcə həqiqət testlərini qurun. Bunları CI-də çalışın. Sizin sayı 4 decimal yerə qarşılaşdıqda, bitirsiniz. Validate with Octave . Get your implementation matching MATLAB before optimizing. A fast wrong answer is still wrong. Once you're accurate, the optimizations are straightforward: TypedArrays, separable filters, buffer reuse. Accuracy first, then performance Bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir neçə ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir ki, bir ildir. Document your differences Akademik MATLAB və istehsal JavaScript arasındakı fərq görünəndən daha azdır. Algoritmalar aynıdır. Matematika aynıdır. Doğru olduğunuzu təsdiq etmək üçün bir yol lazımdır. SSIM və GMSD implementasiyaları bu makalədə [blazediff.dev] (https://blazediff.dev). MIT lisenziyası, zero dependencies, validated against MATLAB.