{"id":871,"date":"2025-06-15T11:49:09","date_gmt":"2025-06-15T09:49:09","guid":{"rendered":"https:\/\/renor.it\/evaluation-of-the-level-of-blur-by-laplacian-variance\/"},"modified":"2025-12-20T23:49:35","modified_gmt":"2025-12-20T22:49:35","slug":"evaluation-of-the-level-of-blur-by-laplacian-variance","status":"publish","type":"post","link":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/","title":{"rendered":"Evaluation of the level of Blur by Laplacian Variance"},"content":{"rendered":"\n<p>Our App for people with celiac disease and food intolerances, <strong>CeliachIA<\/strong>, allows users to instantly check for the presence of gluten in products that are not already database-censored: the user photographs the ingredient list on the label and a <strong>fine-tuned computer vision<\/strong> model processes the image, returning the answer in seconds.<\/p>\n\n<p>During the development phase, we ran into a crucial hurdle: ensuring that the photograph of the ingredients was sharp enough to allow accurate text extraction (OCR) and, consequently, reliable analysis. On the backend, <strong>Google Cloud Vision<\/strong> already provides an OCR confidence indicator; by applying appropriate thresholds we could decide whether to continue with the analysis or reject the image for poor quality. However, delegating this control to the cloud comes at an unnecessary cost; we still pay for the invocation of an external service even when the photo is obviously unusable.  <\/p>\n\n<p>Therefore, to minimize costs, it was essential to move quality control <strong>to the client<\/strong>. We developed a JavaScript function, based on the <strong>Laplacian variance<\/strong>, that evaluates image sharpness in real time: the library can be run directly during camera preview, avoiding sending out-of-focus or blurry shots to the backend. In this way we achieve a double optimization: the user experience improves (the \u201cblurry image\u201d feedback is immediate) and cloud processing costs are significantly reduced.  <\/p>\n\n<h2 class=\"wp-block-heading\">Measuring the sharpness of an image with the Laplacian variance<\/h2>\n\n<p>The perceptual quality of a photograph is highly dependent on sharpness: an image out of focus or affected by motion blur looks unattractive and, in various application areas (computer vision, diagnostics, mobile photography), can compromise the entire automatic analysis process.  <\/p>\n\n<p>There are numerous techniques for quantitatively estimating the level of blur. In this paper, we present a fast approach, free of external dependencies and suitable for real time, based on the <strong>variance of the Laplacian operator<\/strong>. We will start with the theoretical fundamentals and then arrive at a complete implementation in JavaScript, accompanied by a small Web interface for testing purposes.  <\/p>\n\n<p>The ultimate goal will be to build a widget that assigns a score from 1 to 10 to the sharpness of any image uploaded by the user.  <\/p>\n\n<h2 class=\"wp-block-heading\">Sharpness as a frequency issue<\/h2>\n\n<p>Each digital image can be seen as the sum of components at different spatial frequencies:  <\/p>\n\n<ul class=\"wp-block-list\">\n<li><strong>Low frequencies<\/strong> have slow variations, uniform areas, and smooth gradients<\/li>\n\n\n\n<li><strong>High frequencies<\/strong> result in abrupt transitions, edges, fine details.<\/li>\n<\/ul>\n\n<p>A blur acts as a <strong>low-pass filter<\/strong>, that is, it attenuates high frequencies. So the blurrier an image is, the less residual energy we will have in the high-frequency spectrum.   <\/p>\n\n<p>The project will then work on translating this observation into a single number that is easy to interpret.  <\/p>\n\n<h2 class=\"wp-block-heading\">The variance of the Laplacian: from millions of pixels to a single number<\/h2>\n\n<p>Once we apply the Laplacian filter to the image, we get a new matrix where each value represents the local change in light intensity. But how can we condense this information into a <strong>single numerical value<\/strong> that objectively describes how sharp the image is? <\/p>\n\n<p>The simplest and most effective answer is: <strong>calculate the variance of these values<\/strong>.<br \/>Because of what was said before, a well-focused image has many sharp edges and transitions therefore the values of the Laplacian are widely distributed, both positive and negative: there is therefore high variance.<br \/>A blurred image, blurs the edges therefore the values of the Laplacian are close to zero and little variable: there is therefore low variance.  <\/p>\n\n<p>The variance measures precisely how far the values deviate from the mean: the larger this deviation, the more detail and sharpness the image contains.  <\/p>\n\n<h2 class=\"wp-block-heading\">Mathematical definition<\/h2>\n\n<p>Given a Laplacian response matrix <img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/renor.it\/wp-content\/ql-cache\/quicklatex.com-f45bc4676758faced56a31d4afe2804e_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#36;&#76;&#95;&#105;&#36;\" title=\"Rendered by QuickLaTeX.com\" height=\"15\" width=\"16\" style=\"vertical-align: -3px;\"\/>, where <img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/renor.it\/wp-content\/ql-cache\/quicklatex.com-9f6be33c72982af4b393d661ed93d3e4_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#36;&#105;&#36;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"5\" style=\"vertical-align: 0px;\"\/> denotes each valid pixel (excluding the edge), we calculate:<\/p>\n\n<p><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/renor.it\/wp-content\/ql-cache\/quicklatex.com-1e65f0e6abc96cd6a7cbcf0b12139745_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#109;&#117;&#32;&#61;&#32;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#78;&#125;&#115;&#117;&#109;&#95;&#123;&#105;&#61;&#49;&#125;&#94;&#123;&#78;&#125;&#32;&#76;&#95;&#105;&#44;&#32;&#113;&#113;&#117;&#97;&#100;&#32;&#115;&#105;&#103;&#109;&#97;&#94;&#50;&#32;&#61;&#32;&#102;&#114;&#97;&#99;&#123;&#49;&#125;&#123;&#78;&#125;&#115;&#117;&#109;&#95;&#123;&#105;&#61;&#49;&#125;&#94;&#123;&#78;&#125;&#40;&#76;&#95;&#105;&#32;&#45;&#32;&#109;&#117;&#41;&#94;&#50;\" title=\"Rendered by QuickLaTeX.com\" height=\"20\" width=\"523\" style=\"vertical-align: -5px;\"\/><\/p>\n\n<p>Where:<\/p>\n\n<ul class=\"wp-block-list\">\n<li><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/renor.it\/wp-content\/ql-cache\/quicklatex.com-82c54c2016cfb04537f3a6ddd7a692b0_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#36;&#109;&#117;&#36;\" title=\"Rendered by QuickLaTeX.com\" height=\"8\" width=\"24\" style=\"vertical-align: 0px;\"\/> is the average of the Laplacian values;<\/li>\n\n\n\n<li><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/renor.it\/wp-content\/ql-cache\/quicklatex.com-9675ccd83f47754d4044b5cb35fda688_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#36;&#78;&#36;\" title=\"Rendered by QuickLaTeX.com\" height=\"12\" width=\"13\" style=\"vertical-align: 0px;\"\/> is the total number of pixels involved (usually $(w &#8211; 2)(h &#8211; 2)$);<\/li>\n\n\n\n<li><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/renor.it\/wp-content\/ql-cache\/quicklatex.com-5b6996c2cdce5d94d6037c91de4b2f62_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#36;&#115;&#105;&#103;&#109;&#97;&#94;&#50;&#36;\" title=\"Rendered by QuickLaTeX.com\" height=\"19\" width=\"52\" style=\"vertical-align: -4px;\"\/> is the <strong>variance of the Laplacian<\/strong>, i.e., our sharpness indicator.<\/li>\n<\/ul>\n\n<p>Now we need to go from a raw value to an interpretable score.  <\/p>\n\n<p>Values of <img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/renor.it\/wp-content\/ql-cache\/quicklatex.com-5b6996c2cdce5d94d6037c91de4b2f62_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#36;&#115;&#105;&#103;&#109;&#97;&#94;&#50;&#36;\" title=\"Rendered by QuickLaTeX.com\" height=\"19\" width=\"52\" style=\"vertical-align: -4px;\"\/> depend on hardware and environmental variables: resolution, noise, JPEG compression, lens quality. Therefore, it does not make sense to use a fixed threshold: we need to <strong>normalize<\/strong> the value against an empirical range. <\/p>\n\n<p>We define:<\/p>\n\n<ul class=\"wp-block-list\">\n<li><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/renor.it\/wp-content\/ql-cache\/quicklatex.com-326c25ab934da5af2387a852942c9351_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#36;&#84;&#95;&#123;&#109;&#105;&#110;&#125;\" title=\"Rendered by QuickLaTeX.com\" height=\"15\" width=\"38\" style=\"vertical-align: -3px;\"\/>$: typical variance of a very blurry photo \u2192 corresponds to <strong>score 1<\/strong>;<\/li>\n\n\n\n<li><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/renor.it\/wp-content\/ql-cache\/quicklatex.com-98cd1cdde06d8db0f191ed6f2eb2313f_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#36;&#84;&#95;&#123;&#109;&#97;&#120;&#125;\" title=\"Rendered by QuickLaTeX.com\" height=\"15\" width=\"40\" style=\"vertical-align: -3px;\"\/>$: typical variance of a perfectly sharp photo \u2192 corresponds to <strong>score 10<\/strong>.<\/li>\n<\/ul>\n\n<p>Therefore:<\/p>\n\n<p><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/renor.it\/wp-content\/ql-cache\/quicklatex.com-d81939d6821640c1b6ec66f97df8ae2b_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#116;&#101;&#120;&#116;&#123;&#115;&#99;&#111;&#114;&#101;&#125;&#32;&#61;&#32;&#49;&#32;&#43;&#32;&#57;&#32;&#116;&#105;&#109;&#101;&#115;&#32;&#111;&#112;&#101;&#114;&#97;&#116;&#111;&#114;&#110;&#97;&#109;&#101;&#123;&#99;&#108;&#97;&#109;&#112;&#125;&#108;&#101;&#102;&#116;&#40;&#102;&#114;&#97;&#99;&#123;&#115;&#105;&#103;&#109;&#97;&#94;&#50;&#32;&#45;&#32;&#84;&#95;&#123;&#109;&#105;&#110;&#125;&#125;&#123;&#84;&#95;&#123;&#109;&#97;&#120;&#125;&#32;&#45;&#32;&#84;&#95;&#123;&#109;&#105;&#110;&#125;&#125;&#44;&#32;&#48;&#44;&#32;&#49;&#114;&#105;&#103;&#104;&#116;&#41;\" title=\"Rendered by QuickLaTeX.com\" height=\"20\" width=\"702\" style=\"vertical-align: -5px;\"\/><\/p>\n\n<p>After calculation, the value is rounded to the nearest integer to return a score between 1 and 10. This approach also makes the result readable for the end user, as well as useful for software-side control logic. <\/p>\n\n<h2 class=\"wp-block-heading\">Implementation in JavaScript<\/h2>\n\n<p>The algorithm can be implemented in pure JavaScript using the Canvas API to access the pixels of an image uploaded or taken by camera. The following function takes as input an HTML element ( <code>&lt;img&gt;<\/code>,  <code>&lt;canvas&gt;<\/code>,  <code>&lt;video&gt;<\/code>  o  <code>ImageBitmap<\/code>) and returns an object with  <code>score<\/code>  (1-10) e  <code>variance<\/code>.<\/p>\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewbox=\"0 0 54 14\"><g fill=\"none\"><\/g><\/svg><\/span><span role=\"button\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><textarea class=\"code-block-pro-copy-button-textarea\" aria-hidden=\"true\" readonly>\/**\n * Evaluate image sharpness using Laplacian variance.\n * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement|ImageBitmap} source\n * @param {{thresholdMin?: number, thresholdMax?: number}=} opts\n * @return {Promise&lt;{score: number, variance: number}&gt;}\n *\/\nexport async function blurMeter(source, opts = {}) {\n const T_min = opts.thresholdMin ?? 9; \/\/ calibrated: blurry photo\n const T_max = opts.thresholdMax ?? 50; \/\/ calibrated: sharp photo  \n\n  \/\/ 1. Prepare off-screen canvas\n const w = source.width || source.videoWidth || source.naturalWidth;\n const h = source.height || source.videoHeight || source.naturalHeight;\n if (!w || !h) throw new Error(\u201cSource has invalid dimensions\u201d); \n\n  const off = typeof OffscreenCanvas === \u201cfunction\u201d\n? new OffscreenCanvas(w, h)\n: (() =&gt; { const c = document.createElement(\u201ccanvas\u201d); c.width = w; c.height = h; return c; })();\n const ctx = off.getContext(\u201c2d\u201d);\n ctx.drawImage(source, 0, 0, w, h);\n const { data } = ctx.getImageData(0, 0, w, h); \n\n  \/\/ 2. Convert to grayscale\n const gray = new Float32Array(w * h);\n for (let i = 0, j = 0; i &lt; data.length; i += 4, ++j) {\n gray[j] = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];\n } \n\n  \/\/ 3. Apply Laplacian (4-neighbour kernel)\n const lap = new Float32Array(w * h);\n const idx = (x, y) =&gt; y * w + x;\n for (let y = 1; y &lt; h &#8211; 1; ++y) {\n for (let x = 1; x &lt; w &#8211; 1; ++x) {\n const i = idx(x, y);\n lap[i] = 4 * gray[i] &#8211; gray[idx(x &#8211; 1, y)] &#8211; gray[idx(x + 1, y)] &#8211; gray[idx(x, y &#8211; 1)] &#8211; gray[idx(x, y + 1)];\n }\n } \n\n  \/\/ 4. Compute variance\n let sum = 0, sumSq = 0, count = (w &#8211; 2) * (h &#8211; 2);\n for (let y = 1; y &lt; h &#8211; 1; ++y) {\n for (let x = 1; x &lt; w &#8211; 1; ++x) {\n const v = lap[idx(x, y)];\n sum += v;\n sumSq += v * v;\n }\n }\n const mean = sum \/ count;\n const variance = (sumSq \/ count) &#8211; (mean * mean); \n\n  \/\/ 5. Map to score\n const norm = Math.max(0, Math.min(1, (variance &#8211; T_min) \/ (T_max &#8211; T_min));\n const score = Math.round(1 + 9 * norm); \n\n  return { score, variance };\n}<\/textarea><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" viewbox=\"0 0 24 24\"><path d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\"><code><span class=\"line\"><span style=\"color: #6A9955\">\/**<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"> * Evaluate image sharpness using Laplacian variance.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"> * <\/span><span style=\"color: #569CD6\">@param<\/span><span style=\"color: #6A9955\"> <\/span><span style=\"color: #4EC9B0\">{HTMLImageElement|HTMLCanvasElement|HTMLVideoElement|ImageBitmap}<\/span><span style=\"color: #6A9955\"> <\/span><span style=\"color: #9CDCFE\">source<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"> * <\/span><span style=\"color: #569CD6\">@param<\/span><span style=\"color: #6A9955\"> <\/span><span style=\"color: #4EC9B0\">{{thresholdMin?: number, thresholdMax?: number}=}<\/span><span style=\"color: #6A9955\"> <\/span><span style=\"color: #9CDCFE\">opts<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"> * <\/span><span style=\"color: #569CD6\">@return<\/span><span style=\"color: #6A9955\"> <\/span><span style=\"color: #4EC9B0\">{Promise&lt;{score: number, variance: number}&gt;}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"> *\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">export<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">async<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">function<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">blurMeter<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">source<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">opts<\/span><span style=\"color: #D4D4D4\"> = {}) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">T_min<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">opts<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">thresholdMin<\/span><span style=\"color: #D4D4D4\"> ?? <\/span><span style=\"color: #B5CEA8\">9<\/span><span style=\"color: #D4D4D4\">;    <\/span><span style=\"color: #6A9955\">\/\/ calibrated: blurry photo<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">T_max<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">opts<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">thresholdMax<\/span><span style=\"color: #D4D4D4\"> ?? <\/span><span style=\"color: #B5CEA8\">50<\/span><span style=\"color: #D4D4D4\">;   <\/span><span style=\"color: #6A9955\">\/\/ calibrated: sharp photo<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #6A9955\">\/\/ 1. Prepare off-screen canvas<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">w<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">source<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">width<\/span><span style=\"color: #D4D4D4\">  || <\/span><span style=\"color: #9CDCFE\">source<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">videoWidth<\/span><span style=\"color: #D4D4D4\">  || <\/span><span style=\"color: #9CDCFE\">source<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">naturalWidth<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">h<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">source<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">height<\/span><span style=\"color: #D4D4D4\"> || <\/span><span style=\"color: #9CDCFE\">source<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">videoHeight<\/span><span style=\"color: #D4D4D4\"> || <\/span><span style=\"color: #9CDCFE\">source<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">naturalHeight<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (!<\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\"> || !<\/span><span style=\"color: #9CDCFE\">h<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #C586C0\">throw<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">Error<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">\"Source has invalid dimensions\"<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">off<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">typeof<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">OffscreenCanvas<\/span><span style=\"color: #D4D4D4\"> === <\/span><span style=\"color: #CE9178\">\"function\"<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    ? <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">OffscreenCanvas<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">h<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    : (() <\/span><span style=\"color: #569CD6\">=&gt;<\/span><span style=\"color: #D4D4D4\"> { <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">c<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">document<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">createElement<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">\"canvas\"<\/span><span style=\"color: #D4D4D4\">); <\/span><span style=\"color: #9CDCFE\">c<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">width<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\">; <\/span><span style=\"color: #9CDCFE\">c<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">height<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">h<\/span><span style=\"color: #D4D4D4\">; <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">c<\/span><span style=\"color: #D4D4D4\">; })();<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">ctx<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">off<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">getContext<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">\"2d\"<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">ctx<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">drawImage<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">source<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">h<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> { <\/span><span style=\"color: #4FC1FF\">data<\/span><span style=\"color: #D4D4D4\"> } = <\/span><span style=\"color: #9CDCFE\">ctx<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">getImageData<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">h<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #6A9955\">\/\/ 2. Convert to grayscale<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">gray<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">Float32Array<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #9CDCFE\">h<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #569CD6\">let<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">j<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">; <\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #D4D4D4\"> &lt; <\/span><span style=\"color: #9CDCFE\">data<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">length<\/span><span style=\"color: #D4D4D4\">; <\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #D4D4D4\"> += <\/span><span style=\"color: #B5CEA8\">4<\/span><span style=\"color: #D4D4D4\">, ++<\/span><span style=\"color: #9CDCFE\">j<\/span><span style=\"color: #D4D4D4\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">gray<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #9CDCFE\">j<\/span><span style=\"color: #D4D4D4\">] = <\/span><span style=\"color: #B5CEA8\">0.299<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #9CDCFE\">data<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #D4D4D4\">] + <\/span><span style=\"color: #B5CEA8\">0.587<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #9CDCFE\">data<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">] + <\/span><span style=\"color: #B5CEA8\">0.114<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #9CDCFE\">data<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">];<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  }<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #6A9955\">\/\/ 3. Apply Laplacian (4-neighbour kernel)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">lap<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">Float32Array<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #9CDCFE\">h<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">idx<\/span><span style=\"color: #D4D4D4\"> = (<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #569CD6\">=&gt;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #569CD6\">let<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">; <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\"> &lt; <\/span><span style=\"color: #9CDCFE\">h<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">; ++<\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #569CD6\">let<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">; <\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\"> &lt; <\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">; ++<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">i<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #DCDCAA\">idx<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">lap<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #D4D4D4\">] = <\/span><span style=\"color: #B5CEA8\">4<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #9CDCFE\">gray<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #9CDCFE\">i<\/span><span style=\"color: #D4D4D4\">] - <\/span><span style=\"color: #9CDCFE\">gray<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #DCDCAA\">idx<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\">)] - <\/span><span style=\"color: #9CDCFE\">gray<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #DCDCAA\">idx<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\">)] - <\/span><span style=\"color: #9CDCFE\">gray<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #DCDCAA\">idx<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">)] - <\/span><span style=\"color: #9CDCFE\">gray<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #DCDCAA\">idx<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">)];<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  }<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #6A9955\">\/\/ 4. Compute variance<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">let<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">sum<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">sumSq<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">count<\/span><span style=\"color: #D4D4D4\"> = (<\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">) * (<\/span><span style=\"color: #9CDCFE\">h<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #569CD6\">let<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">; <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\"> &lt; <\/span><span style=\"color: #9CDCFE\">h<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">; ++<\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #569CD6\">let<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">; <\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\"> &lt; <\/span><span style=\"color: #9CDCFE\">w<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">; ++<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">v<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">lap<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #DCDCAA\">idx<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">x<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">y<\/span><span style=\"color: #D4D4D4\">)];<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">sum<\/span><span style=\"color: #D4D4D4\"> += <\/span><span style=\"color: #9CDCFE\">v<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">sumSq<\/span><span style=\"color: #D4D4D4\"> += <\/span><span style=\"color: #9CDCFE\">v<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #9CDCFE\">v<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">mean<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">sum<\/span><span style=\"color: #D4D4D4\"> \/ <\/span><span style=\"color: #9CDCFE\">count<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">variance<\/span><span style=\"color: #D4D4D4\"> = (<\/span><span style=\"color: #9CDCFE\">sumSq<\/span><span style=\"color: #D4D4D4\"> \/ <\/span><span style=\"color: #9CDCFE\">count<\/span><span style=\"color: #D4D4D4\">) - (<\/span><span style=\"color: #9CDCFE\">mean<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #9CDCFE\">mean<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #6A9955\">\/\/ 5. Map to score<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">norm<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">Math<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">max<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">Math<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">min<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">, (<\/span><span style=\"color: #9CDCFE\">variance<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #9CDCFE\">T_min<\/span><span style=\"color: #D4D4D4\">) \/ (<\/span><span style=\"color: #9CDCFE\">T_max<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #9CDCFE\">T_min<\/span><span style=\"color: #D4D4D4\">)));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4FC1FF\">score<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">Math<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">round<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #B5CEA8\">9<\/span><span style=\"color: #D4D4D4\"> * <\/span><span style=\"color: #9CDCFE\">norm<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> { <\/span><span style=\"color: #9CDCFE\">score<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">variance<\/span><span style=\"color: #D4D4D4\"> };<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n<p>This function is lightweight, self-contained, and can be performed directly client-side, such as in a live camera web application or image analysis.<\/p>\n\n<p>Next step: we create a simple interface with HTML and Tailwind to use this function interactively.<\/p>\n\n<h2 class=\"wp-block-heading\">HTML + Tailwind interface<\/h2>\n\n<p>We now consume the function in a simple user interface made in HTML and Tailwind that allows the user to load an image, preview it, and immediately receive a sharpness score with a colored bar from red (blurry) to green (sharp).<\/p>\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewbox=\"0 0 54 14\"><g fill=\"none\"><\/g><\/svg><\/span><span role=\"button\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><textarea class=\"code-block-pro-copy-button-textarea\" aria-hidden=\"true\" readonly>&lt;!DOCTYPE html&gt;\n&lt;html lang=\u201cen\u201d&gt;\n&lt;head&gt;\n  &lt;meta charset=\u201cUTF-8\u201d&gt;\n  &lt;title&gt;Blur Meter Demo&lt;\/title&gt;\n  &lt;!&#8211; Tailwind CSS via CDN &#8211;&gt;\n  &lt;script src=\u201chttps:\/\/cdn.tailwindcss.com\u201d&gt;&lt;\/script&gt;\n  &lt;meta name=\u201cviewport\u201d content=\u201cwidth=device-width, initial-scale=1\u201d&gt;\n&lt;\/head&gt;\n&lt;body class=\u201cmin-h-screen bg-slate-100 flex items-center justify-center p-4\u201d&gt;\n  &lt;div class=\u201cw-full max-w-lg bg-white shadow-xl rounded-2xl p-6 space-y-6\u201d&gt;\n    &lt;h1 class=\u201ctext-2xl font-semibold text-center\u201d&gt;Image Blur Meter&lt;\/h1&gt;\n\n    &lt;!&#8211; File input &#8211;&gt;\n    &lt;label class=\u201cflex flex-col items-center justify-center w-full h-40 px-4 transition bg-white border-2 border-dashed rounded-lg cursor-pointer hover:border-indigo-500\u201d&gt;\n      &lt;span class=\u201ctext-sm text-gray-500\u201d&gt;Click here to upload an image&lt;\/span&gt;\n      &lt;input id=\u201cuploader\u201d type=\u201cfile\u201d accept=\u201cimage\/*\u201d class=\u201chidden\u201d&gt;\n    &lt;\/label&gt;\n\n    &lt;!&#8211; Image preview &#8211;&gt;\n    &lt;div id=\u201cpreviewWrapper\u201d class=\u201chidden\u201d&gt;\n      &lt;img id=\u201cpreview\u201d alt=\u201cpreview\u201d class=\u201cmx-auto max-h-64 rounded-lg shadow-md\u201d\/&gt;\n    &lt;\/div&gt;\n\n    &lt;!&#8211; Results &#8211;&gt;\n    &lt;div id=\u201cresult\u201d class=\u201chidden space-y-3\u201d&gt;\n      &lt;p id=\u201cscoreText\u201d class=\u201ctext-center text-lg font-medium\u201d&gt;&lt;\/p&gt;\n\n      &lt;!&#8211; Progress bar &#8211;&gt;\n      &lt;div class=\u201cw-full bg-gray-200 rounded-full h-4\u201d&gt;\n        &lt;div id=\u201cscoreBar\u201d class=\u201ch-4 rounded-full transition-all duration-500\u201d style=\u201cwidth:0%\u201d&gt;&lt;\/div&gt;\n      &lt;\/div&gt;\n\n      &lt;p id=\u201cvarianceText\u201d class=\u201ctext-center text-sm text-gray-500\u201d&gt;&lt;\/p&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n\n  &lt;script type=\u201cmodule\u201d&gt;\n import { blurMeter } from \u201c.\/js\/blur-meter.js\u201d;\n\n  const uploader = document.getElementById(\u201cuploader\u201d);\n const preview = document.getElementById(\u201cpreview\u201d);\n const previewWrap = document.getElementById(\u201cpreviewWrapper\u201d);\n const scoreText = document.getElementById(\u201cscoreText\u201d);\n const varianceText = document.getElementById(\u201cvarianceText\u201d);\n const scoreBar = document.getElementById(\u201cscoreBar\u201d);\n const resultBlock = document.getElementById(\u201cresult\u201d);\n\n  const scoreToColor = score =&gt; {\n const t = (score &#8211; 1) \/ 9;\n const r = Math.round(220 + (22 &#8211; 220) * t);\n const g = Math.round( 38 + (163 &#8211; 38) * t);\n const b = Math.round( 38 + ( 74 &#8211; 38) * t);\n return `rgb(${r},${g},${b})`;\n };\n\n  uploader.addEventListener(\u201cchange\u201d, async e =&gt; {\n const file = e.target.files[0];\n if (!file) return;\n\n  const url = URL.createObjectURL(file);\n preview.src = url;\n previewWrap.classList.remove(\u201chidden\u201d);\n\n  await new Promise(res =&gt; preview.onload = res);\n\n  const { score, variance } = await blurMeter(preview);\n\n  scoreText.textContent = `Sharpness Score: ${score}\/10`;\n varianceText.textContent = `Laplacian variance: ${variance.toFixed(2)}`;\n const percent = ((score &#8211; 1) \/ 9) * 100;\n scoreBar.style.width = percent + \u201c%\u201d;\n scoreBar.style.backgroundColor = scoreToColor(score);\n resultBlock.classList.remove(\u201chidden\u201d);\n\n  URL.revokeObjectURL(url);\n });\n  &lt;\/script&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/textarea><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" viewbox=\"0 0 24 24\"><path d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\"><code><span class=\"line\"><span style=\"color: #D4D4D4\">&lt;!<\/span><span style=\"color: #4FC1FF\">DOCTYPE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">html<\/span><span style=\"color: #D4D4D4\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">html<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">lang<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"it\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">head<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">meta<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">charset<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"UTF-8\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">title<\/span><span style=\"color: #808080\">&gt;<\/span><span style=\"color: #D4D4D4\">Blur Meter Demo<\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">title<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  &lt;!-- Tailwind CSS via CDN --&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">script<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">src<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"https:\/\/cdn.tailwindcss.com\"<\/span><span style=\"color: #808080\">&gt;&lt;\/<\/span><span style=\"color: #569CD6\">script<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">meta<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">name<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"viewport\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">content<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"width=device-width, initial-scale=1\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">head<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">body<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"min-h-screen bg-slate-100 flex items-center justify-center p-4\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">div<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"w-full max-w-lg bg-white shadow-xl rounded-2xl p-6 space-y-6\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">h1<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"text-2xl font-semibold text-center\"<\/span><span style=\"color: #808080\">&gt;<\/span><span style=\"color: #D4D4D4\">Image Blur Meter<\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">h1<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    &lt;!-- File input --&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">label<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"flex flex-col items-center justify-center w-full h-40 px-4 transition bg-white border-2 border-dashed rounded-lg cursor-pointer hover:border-indigo-500\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">span<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"text-sm text-gray-500\"<\/span><span style=\"color: #808080\">&gt;<\/span><span style=\"color: #D4D4D4\">Click here to upload an image<\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">span<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">input<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">id<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"uploader\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">type<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"file\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">accept<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"image\/*\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"hidden\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">label<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    &lt;!-- Image preview --&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">div<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">id<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"previewWrapper\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"hidden\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">img<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">id<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"preview\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">alt<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"preview\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"mx-auto max-h-64 rounded-lg shadow-md\"<\/span><span style=\"color: #808080\">\/&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">div<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    &lt;!-- Results --&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">div<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">id<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"result\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"hidden space-y-3\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">p<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">id<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"scoreText\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"text-center text-lg font-medium\"<\/span><span style=\"color: #808080\">&gt;&lt;\/<\/span><span style=\"color: #569CD6\">p<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      &lt;!-- Progress bar --&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">div<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"w-full bg-gray-200 rounded-full h-4\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">div<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">id<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"scoreBar\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"h-4 rounded-full transition-all duration-500\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">style<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"width:0%\"<\/span><span style=\"color: #808080\">&gt;&lt;\/<\/span><span style=\"color: #569CD6\">div<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">div<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">p<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">id<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"varianceText\"<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">class<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"text-center text-sm text-gray-500\"<\/span><span style=\"color: #808080\">&gt;&lt;\/<\/span><span style=\"color: #569CD6\">p<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">div<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">div<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;<\/span><span style=\"color: #569CD6\">script<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">type<\/span><span style=\"color: #D4D4D4\">=<\/span><span style=\"color: #CE9178\">\"module\"<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    import <\/span><span style=\"color: #569CD6\">{<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">blurMeter<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">}<\/span><span style=\"color: #D4D4D4\"> from '.\/js\/blur-meter.js';<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    const uploader      = document.getElementById('uploader');<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    const preview       = document.getElementById('preview');<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    const previewWrap   = document.getElementById('previewWrapper');<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    const scoreText     = document.getElementById('scoreText');<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    const varianceText  = document.getElementById('varianceText');<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    const scoreBar      = document.getElementById('scoreBar');<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    const resultBlock   = document.getElementById('result');<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    const scoreToColor = score =&gt; <\/span><span style=\"color: #569CD6\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">t<\/span><span style=\"color: #D4D4D4\"> = (<\/span><span style=\"color: #9CDCFE\">score<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">) \/ <\/span><span style=\"color: #B5CEA8\">9<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">r<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">Math<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">round<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">220<\/span><span style=\"color: #D4D4D4\"> + (<\/span><span style=\"color: #B5CEA8\">22<\/span><span style=\"color: #D4D4D4\">  - <\/span><span style=\"color: #B5CEA8\">220<\/span><span style=\"color: #D4D4D4\">) * <\/span><span style=\"color: #9CDCFE\">t<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">g<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">Math<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">round<\/span><span style=\"color: #D4D4D4\">( <\/span><span style=\"color: #B5CEA8\">38<\/span><span style=\"color: #D4D4D4\"> + (<\/span><span style=\"color: #B5CEA8\">163<\/span><span style=\"color: #D4D4D4\"> -  <\/span><span style=\"color: #B5CEA8\">38<\/span><span style=\"color: #D4D4D4\">) * <\/span><span style=\"color: #9CDCFE\">t<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">b<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">Math<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">round<\/span><span style=\"color: #D4D4D4\">( <\/span><span style=\"color: #B5CEA8\">38<\/span><span style=\"color: #D4D4D4\"> + ( <\/span><span style=\"color: #B5CEA8\">74<\/span><span style=\"color: #D4D4D4\"> -  <\/span><span style=\"color: #B5CEA8\">38<\/span><span style=\"color: #D4D4D4\">) * <\/span><span style=\"color: #9CDCFE\">t<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">return<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">`rgb(<\/span><span style=\"color: #569CD6\">${<\/span><span style=\"color: #9CDCFE\">r<\/span><span style=\"color: #569CD6\">}<\/span><span style=\"color: #CE9178\">,<\/span><span style=\"color: #569CD6\">${<\/span><span style=\"color: #9CDCFE\">g<\/span><span style=\"color: #569CD6\">}<\/span><span style=\"color: #CE9178\">,<\/span><span style=\"color: #569CD6\">${<\/span><span style=\"color: #9CDCFE\">b<\/span><span style=\"color: #569CD6\">}<\/span><span style=\"color: #CE9178\">)`<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">}<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    uploader.addEventListener('change', async e =&gt; <\/span><span style=\"color: #569CD6\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">file<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">e<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">target<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">files<\/span><span style=\"color: #D4D4D4\">[<\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">];<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #DCDCAA\">if<\/span><span style=\"color: #D4D4D4\"> (!<\/span><span style=\"color: #9CDCFE\">file<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #9CDCFE\">return<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">url<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #4FC1FF\">URL<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">createObjectURL<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">file<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">preview<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">src<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">url<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">previewWrap<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">classList<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">remove<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">'hidden'<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">await<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">new<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Promise<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">res<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">=&gt;<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">preview<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">onload<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">res<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">const<\/span><span style=\"color: #D4D4D4\"> { <\/span><span style=\"color: #9CDCFE\">score<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">variance<\/span><span style=\"color: #D4D4D4\"> } = <\/span><span style=\"color: #C586C0\">await<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">blurMeter<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">preview<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">scoreText<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">textContent<\/span><span style=\"color: #D4D4D4\">    = <\/span><span style=\"color: #CE9178\">`Sharpness Score: <\/span><span style=\"color: #569CD6\">${<\/span><span style=\"color: #9CDCFE\">score<\/span><span style=\"color: #569CD6\">}<\/span><span style=\"color: #CE9178\">\/10`<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">varianceText<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">textContent<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">`Laplacian variance: <\/span><span style=\"color: #569CD6\">${<\/span><span style=\"color: #9CDCFE\">variance<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">toFixed<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">)<\/span><span style=\"color: #569CD6\">}<\/span><span style=\"color: #CE9178\">`<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">const<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">percent<\/span><span style=\"color: #D4D4D4\">            = ((<\/span><span style=\"color: #9CDCFE\">score<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">) \/ <\/span><span style=\"color: #B5CEA8\">9<\/span><span style=\"color: #D4D4D4\">) * <\/span><span style=\"color: #B5CEA8\">100<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">scoreBar<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">style<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">width<\/span><span style=\"color: #D4D4D4\">     = <\/span><span style=\"color: #9CDCFE\">percent<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #CE9178\">'%'<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">scoreBar<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">style<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">backgroundColor<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #DCDCAA\">scoreToColor<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">score<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">resultBlock<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #9CDCFE\">classList<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">remove<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">'hidden'<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\" \/>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #4FC1FF\">URL<\/span><span style=\"color: #D4D4D4\">.<\/span><span style=\"color: #DCDCAA\">revokeObjectURL<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">url<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">}<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">script<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">body<\/span><span style=\"color: #808080\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #808080\">&lt;\/<\/span><span style=\"color: #569CD6\">html<\/span><span style=\"color: #808080\">&gt;<\/span><\/span><\/code><\/pre><\/div>\n\n<h2 class=\"wp-block-heading\">Functional testing<\/h2>\n\n<p>We tested the function on three different types of image&#8230;<\/p>\n\n<h3 class=\"wp-block-heading\">Completely out of focus<\/h3>\n\n<figure class=\"wp-block-image size-full\"><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" width=\"550\" height=\"692\" src=\"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-2.webp\" alt=\"\" class=\"wp-image-858\" srcset=\"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-2.webp 550w, https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-2-238x300.webp 238w\" sizes=\"auto, (max-width: 550px) 100vw, 550px\" \/><\/figure>\n\n<h3 class=\"wp-block-heading\">Moderately blurred<\/h3>\n\n<figure class=\"wp-block-image size-full\"><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" width=\"551\" height=\"690\" src=\"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-3.webp\" alt=\"\" class=\"wp-image-860\" srcset=\"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-3.webp 551w, https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-3-240x300.webp 240w\" sizes=\"auto, (max-width: 551px) 100vw, 551px\" \/><\/figure>\n\n<h3 class=\"wp-block-heading\">Sharp<\/h3>\n\n<figure class=\"wp-block-image size-full\"><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" width=\"559\" height=\"692\" src=\"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-4.webp\" alt=\"\" class=\"wp-image-862\" srcset=\"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-4.webp 559w, https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-4-242x300.webp 242w\" sizes=\"auto, (max-width: 559px) 100vw, 559px\" \/><\/figure>\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n<p>The Laplacian variance proves to be a simple but extremely powerful and effective tool for assessing the sharpness of an image in real-time client-side contexts. Its strength lies in its computational speed, absence of external dependencies and consistency with human visual perception.   <\/p>\n\n<p>In our use case, applied to the CeliachIA App, this metric allowed us to anticipate quality control directly on the user&#8217;s device, avoiding unnecessary costs of cloud invocations when images are not suitable for OCR analysis.  <\/p>\n\n<p>The pipeline built with JavaScript and Canvas API, coupled with a simple Web interface developed with Tailwind, is a concredo example of how mathematical concepts and engineering tools can come together in a robust, cost-effective, and user-friendly solution.  <\/p>\n\n<p>Ultimately, having an automated sharpness scoring system is not just a technical quirk, but an essential component of ensuring quality, efficiency, and sustainability in modern image analysis pipelines.  <\/p>\n\n<p>You can download this tool from my GitHub account at the link: <a href=\"https:\/\/github.com\/thesimon82\/Laplacian-Blur-Detector\/\">https:\/\/github.com\/thesimon82\/Laplacian-Blur-Detector\/<\/a><\/p>\n\n<p>[starbox]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Our App for people with celiac disease and food intolerances, CeliachIA, allows users to instantly check for the presence of gluten in products that are not already database-censored: the user photographs the ingredient list on the label and a fine-tuned computer vision model processes the image, returning the answer in seconds. During the development phase, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":866,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_writerflow_disable_suggestions":false,"footnotes":""},"categories":[1983],"tags":[1907,1908,1909,1910,1911,1350,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925],"class_list":["post-871","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-artificial-intelligence-algorithms","tag-blur-detection","tag-blur-metrics","tag-canvas-api-en","tag-celiac-applications","tag-client-side-ocr","tag-computer-vision-en","tag-frontend-side-image-validation","tag-image-analysis-in-javascript","tag-image-quality","tag-image-sharpness","tag-laplacian-filter","tag-ocr-optimization","tag-photo-quality-control","tag-photography-app","tag-real-time-image-analysis","tag-sharpness-measurement","tag-sharpness-score","tag-variance-of-the-laplacian","tag-web-image-algorithm","tag-web-based-image-processing"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.4 (Yoast SEO v27.6) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Evaluation of the level of Blur by Laplacian Variance | RENOR &amp; Partners S.r.l.<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Evaluation of the level of Blur by Laplacian Variance\" \/>\n<meta property=\"og:description\" content=\"Our App for people with celiac disease and food intolerances, CeliachIA, allows users to instantly check for the presence of gluten in products that are not already database-censored: the user photographs the ingredient list on the label and a fine-tuned computer vision model processes the image, returning the answer in seconds. During the development phase, [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/\" \/>\n<meta property=\"og:site_name\" content=\"RENOR &amp; Partners S.r.l.\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/renorsrl\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/simone.renzi.3954\/\" \/>\n<meta property=\"article:published_time\" content=\"2025-06-15T09:49:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-12-20T22:49:35+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-5-1.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"1536\" \/>\n\t<meta property=\"og:image:height\" content=\"1024\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"author\" content=\"Simone Renzi\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Simone Renzi\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/\"},\"author\":{\"name\":\"Simone Renzi\",\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/#\\\/schema\\\/person\\\/21343be04e5983a87f3a9a6182cf8795\"},\"headline\":\"Evaluation of the level of Blur by Laplacian Variance\",\"datePublished\":\"2025-06-15T09:49:09+00:00\",\"dateModified\":\"2025-12-20T22:49:35+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/\"},\"wordCount\":1820,\"publisher\":{\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/renor.it\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/image-5-1.webp\",\"keywords\":[\"blur detection\",\"blur metrics\",\"Canvas API\",\"celiac applications\",\"Client-side OCR\",\"computer vision\",\"frontend side image validation\",\"image analysis in JavaScript\",\"image quality\",\"image sharpness\",\"Laplacian filter\",\"OCR optimization\",\"photo quality control\",\"photography app\",\"real-time image analysis\",\"sharpness measurement\",\"sharpness score\",\"variance of the Laplacian\",\"web image algorithm\",\"web-based image processing\"],\"articleSection\":[\"Artificial Intelligence &amp; Algorithms\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/\",\"url\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/\",\"name\":\"Evaluation of the level of Blur by Laplacian Variance | RENOR &amp; Partners S.r.l.\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/renor.it\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/image-5-1.webp\",\"datePublished\":\"2025-06-15T09:49:09+00:00\",\"dateModified\":\"2025-12-20T22:49:35+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/#primaryimage\",\"url\":\"https:\\\/\\\/renor.it\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/image-5-1.webp\",\"contentUrl\":\"https:\\\/\\\/renor.it\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/image-5-1.webp\",\"width\":1536,\"height\":1024,\"caption\":\"Interfaccia dell\u2019applicazione \u201cImage Blur Meter\u201d durante l\u2019analisi di una fotografia: viene mostrata l\u2019immagine caricata, un punteggio di nitidezza su scala 1\u201310 e il valore grezzo della varianza del Laplaciano.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/evaluation-of-the-level-of-blur-by-laplacian-variance\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/renor.it\\\/en\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Blog\",\"item\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Artificial Intelligence &amp; Algorithms\",\"item\":\"https:\\\/\\\/renor.it\\\/en\\\/blog\\\/artificial-intelligence-algorithms\\\/\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Evaluation of the level of Blur by Laplacian Variance\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/renor.it\\\/en\\\/\",\"name\":\"RENOR & Partners S.r.l.\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/#organization\"},\"alternateName\":\"RENOR & Partners S.r.l.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/renor.it\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/#organization\",\"name\":\"RENOR & Partners S.r.l.\",\"url\":\"https:\\\/\\\/renor.it\\\/en\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/renor.it\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/logo-new-1.webp\",\"contentUrl\":\"https:\\\/\\\/renor.it\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/logo-new-1.webp\",\"width\":432,\"height\":146,\"caption\":\"RENOR & Partners S.r.l.\"},\"image\":{\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/renorsrl\",\"https:\\\/\\\/www.instagram.com\\\/renorpartners\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/renor-partners\\\/posts\\\/?feedView=all\"],\"description\":\"RENOR & Partners Srl \u00e8 una societ\u00e0 di consulenza tecnologica e ingegneristica specializzata in sviluppo software, cloud computing, integrazione di sistemi, intelligenza artificiale applicata e progettazione elettronica. L\u2019azienda supporta imprese e pubbliche amministrazioni nella realizzazione di soluzioni digitali affidabili, scalabili e orientate all\u2019efficienza, con un approccio pragmatico basato su competenze tecniche, progettazione su misura e innovazione concreta.\",\"email\":\"info@renor.it\",\"telephone\":\"3791489430\",\"legalName\":\"RENOR AND PARTNERS S.r.l.\",\"foundingDate\":\"2022-06-21\",\"vatID\":\"16768411007\",\"taxID\":\"16768411007\",\"numberOfEmployees\":{\"@type\":\"QuantitativeValue\",\"minValue\":\"1\",\"maxValue\":\"10\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/renor.it\\\/en\\\/#\\\/schema\\\/person\\\/21343be04e5983a87f3a9a6182cf8795\",\"name\":\"Simone Renzi\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/54f81b51be6bda6d63a06a1cd6563d9b0d5778d7af4f0bda4e246fc3e5737e2e?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/54f81b51be6bda6d63a06a1cd6563d9b0d5778d7af4f0bda4e246fc3e5737e2e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/54f81b51be6bda6d63a06a1cd6563d9b0d5778d7af4f0bda4e246fc3e5737e2e?s=96&d=mm&r=g\",\"caption\":\"Simone Renzi\"},\"description\":\"Senior full-stack web engineer with over 20 years of experience in cloud architectures, AI, and SaaS solutions; member of Mensa Italia. Creator of platforms such as HR24.ai and Paghe.ai, he oversaw the web development of FNS, a neural network simulator cited in Scientific Reports (Nature Portfolio), and has collaborated on research projects with INFN \u2013 Laboratori Nazionali di Frascati, Universit\u00e0 di Roma \u201cTor Vergata\u201d, Universidad Complutense, Universidad Polit\u00e9cnica and Centro de Tecnolog\u00eda Biom\u00e9dica in Madrid. A classical pianist, he combines musical creativity and technological rigor in every project.\",\"sameAs\":[\"https:\\\/\\\/renor.it\",\"https:\\\/\\\/www.facebook.com\\\/simone.renzi.3954\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/simone-renzi\"],\"url\":\"https:\\\/\\\/renor.it\\\/en\\\/author\\\/thesimon\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Evaluation of the level of Blur by Laplacian Variance | RENOR &amp; Partners S.r.l.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/","og_locale":"en_US","og_type":"article","og_title":"Evaluation of the level of Blur by Laplacian Variance","og_description":"Our App for people with celiac disease and food intolerances, CeliachIA, allows users to instantly check for the presence of gluten in products that are not already database-censored: the user photographs the ingredient list on the label and a fine-tuned computer vision model processes the image, returning the answer in seconds. During the development phase, [&hellip;]","og_url":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/","og_site_name":"RENOR &amp; Partners S.r.l.","article_publisher":"https:\/\/www.facebook.com\/renorsrl","article_author":"https:\/\/www.facebook.com\/simone.renzi.3954\/","article_published_time":"2025-06-15T09:49:09+00:00","article_modified_time":"2025-12-20T22:49:35+00:00","og_image":[{"width":1536,"height":1024,"url":"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-5-1.webp","type":"image\/webp"}],"author":"Simone Renzi","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Simone Renzi","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/#article","isPartOf":{"@id":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/"},"author":{"name":"Simone Renzi","@id":"https:\/\/renor.it\/en\/#\/schema\/person\/21343be04e5983a87f3a9a6182cf8795"},"headline":"Evaluation of the level of Blur by Laplacian Variance","datePublished":"2025-06-15T09:49:09+00:00","dateModified":"2025-12-20T22:49:35+00:00","mainEntityOfPage":{"@id":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/"},"wordCount":1820,"publisher":{"@id":"https:\/\/renor.it\/en\/#organization"},"image":{"@id":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/#primaryimage"},"thumbnailUrl":"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-5-1.webp","keywords":["blur detection","blur metrics","Canvas API","celiac applications","Client-side OCR","computer vision","frontend side image validation","image analysis in JavaScript","image quality","image sharpness","Laplacian filter","OCR optimization","photo quality control","photography app","real-time image analysis","sharpness measurement","sharpness score","variance of the Laplacian","web image algorithm","web-based image processing"],"articleSection":["Artificial Intelligence &amp; Algorithms"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/","url":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/","name":"Evaluation of the level of Blur by Laplacian Variance | RENOR &amp; Partners S.r.l.","isPartOf":{"@id":"https:\/\/renor.it\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/#primaryimage"},"image":{"@id":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/#primaryimage"},"thumbnailUrl":"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-5-1.webp","datePublished":"2025-06-15T09:49:09+00:00","dateModified":"2025-12-20T22:49:35+00:00","breadcrumb":{"@id":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/#primaryimage","url":"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-5-1.webp","contentUrl":"https:\/\/renor.it\/wp-content\/uploads\/2025\/06\/image-5-1.webp","width":1536,"height":1024,"caption":"Interfaccia dell\u2019applicazione \u201cImage Blur Meter\u201d durante l\u2019analisi di una fotografia: viene mostrata l\u2019immagine caricata, un punteggio di nitidezza su scala 1\u201310 e il valore grezzo della varianza del Laplaciano."},{"@type":"BreadcrumbList","@id":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/evaluation-of-the-level-of-blur-by-laplacian-variance\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/renor.it\/en\/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https:\/\/renor.it\/en\/blog\/"},{"@type":"ListItem","position":3,"name":"Artificial Intelligence &amp; Algorithms","item":"https:\/\/renor.it\/en\/blog\/artificial-intelligence-algorithms\/"},{"@type":"ListItem","position":4,"name":"Evaluation of the level of Blur by Laplacian Variance"}]},{"@type":"WebSite","@id":"https:\/\/renor.it\/en\/#website","url":"https:\/\/renor.it\/en\/","name":"RENOR & Partners S.r.l.","description":"","publisher":{"@id":"https:\/\/renor.it\/en\/#organization"},"alternateName":"RENOR & Partners S.r.l.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/renor.it\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/renor.it\/en\/#organization","name":"RENOR & Partners S.r.l.","url":"https:\/\/renor.it\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/renor.it\/en\/#\/schema\/logo\/image\/","url":"https:\/\/renor.it\/wp-content\/uploads\/2025\/12\/logo-new-1.webp","contentUrl":"https:\/\/renor.it\/wp-content\/uploads\/2025\/12\/logo-new-1.webp","width":432,"height":146,"caption":"RENOR & Partners S.r.l."},"image":{"@id":"https:\/\/renor.it\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/renorsrl","https:\/\/www.instagram.com\/renorpartners\/","https:\/\/www.linkedin.com\/company\/renor-partners\/posts\/?feedView=all"],"description":"RENOR & Partners Srl \u00e8 una societ\u00e0 di consulenza tecnologica e ingegneristica specializzata in sviluppo software, cloud computing, integrazione di sistemi, intelligenza artificiale applicata e progettazione elettronica. L\u2019azienda supporta imprese e pubbliche amministrazioni nella realizzazione di soluzioni digitali affidabili, scalabili e orientate all\u2019efficienza, con un approccio pragmatico basato su competenze tecniche, progettazione su misura e innovazione concreta.","email":"info@renor.it","telephone":"3791489430","legalName":"RENOR AND PARTNERS S.r.l.","foundingDate":"2022-06-21","vatID":"16768411007","taxID":"16768411007","numberOfEmployees":{"@type":"QuantitativeValue","minValue":"1","maxValue":"10"}},{"@type":"Person","@id":"https:\/\/renor.it\/en\/#\/schema\/person\/21343be04e5983a87f3a9a6182cf8795","name":"Simone Renzi","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/54f81b51be6bda6d63a06a1cd6563d9b0d5778d7af4f0bda4e246fc3e5737e2e?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/54f81b51be6bda6d63a06a1cd6563d9b0d5778d7af4f0bda4e246fc3e5737e2e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/54f81b51be6bda6d63a06a1cd6563d9b0d5778d7af4f0bda4e246fc3e5737e2e?s=96&d=mm&r=g","caption":"Simone Renzi"},"description":"Senior full-stack web engineer with over 20 years of experience in cloud architectures, AI, and SaaS solutions; member of Mensa Italia. Creator of platforms such as HR24.ai and Paghe.ai, he oversaw the web development of FNS, a neural network simulator cited in Scientific Reports (Nature Portfolio), and has collaborated on research projects with INFN \u2013 Laboratori Nazionali di Frascati, Universit\u00e0 di Roma \u201cTor Vergata\u201d, Universidad Complutense, Universidad Polit\u00e9cnica and Centro de Tecnolog\u00eda Biom\u00e9dica in Madrid. A classical pianist, he combines musical creativity and technological rigor in every project.","sameAs":["https:\/\/renor.it","https:\/\/www.facebook.com\/simone.renzi.3954\/","https:\/\/www.linkedin.com\/in\/simone-renzi"],"url":"https:\/\/renor.it\/en\/author\/thesimon\/"}]}},"_links":{"self":[{"href":"https:\/\/renor.it\/en\/wp-json\/wp\/v2\/posts\/871","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/renor.it\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/renor.it\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/renor.it\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/renor.it\/en\/wp-json\/wp\/v2\/comments?post=871"}],"version-history":[{"count":0,"href":"https:\/\/renor.it\/en\/wp-json\/wp\/v2\/posts\/871\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/renor.it\/en\/wp-json\/wp\/v2\/media\/866"}],"wp:attachment":[{"href":"https:\/\/renor.it\/en\/wp-json\/wp\/v2\/media?parent=871"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/renor.it\/en\/wp-json\/wp\/v2\/categories?post=871"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/renor.it\/en\/wp-json\/wp\/v2\/tags?post=871"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}