栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

HTML5画布调整大小(缩小)图像的质量?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

HTML5画布调整大小(缩小)图像的质量?

由于你的问题是缩小图像,因此谈论插值(即创建像素)毫无意义。这里的问题是下采样。

要对图像进行降采样,我们需要将原始图像中的每个p * p像素正方形变成目标图像中的单个像素。

出于性能原因,浏览器进行了非常简单的下采样:要生成较小的图像,它们将仅在源中选择一个像素并将其值用作目标。这会“忘记”一些细节并增加噪音。

但是有一个例外:由于2X图像下采样的计算非常简单(平均4个像素即可制作一个),并且用于视网膜/ HiDPI像素,因此这种情况得到了正确处理-浏览器确实使用4个像素来制作一-。

但是…如果你多次使用2X下采样,则会遇到连续的舍入误差会增加过多噪声的问题。
更糟糕的是,你将无法始终将大小调整为2的幂,并且将大小调整为最接近的幂+最后一次调整大小会非常嘈杂。

你要寻找的是像素完美的下采样,即:对图像进行重新采样,无论尺寸如何,都将考虑所有输入像素。
为此,我们必须针对每个输入像素计算其对一个,两个或四个目标像素的贡献,具体取决于输入像素的缩放投影是否恰好在目标像素内部,与X边界,Y边界或两者重叠。
(一个计划在这里会很好,但是我没有一个。)

这是一个画布比例与我的像素完美比例(在1/3缩放比例下)的示例。

请注意,图片可能会在浏览器中缩放,并以.jpeg格式化。
但是,我们看到的噪音要少得多,尤其是在袋熊后面的草丛中以及在其右边的树枝中。皮毛上的噪音使它更具反差,但看起来他像白发(与原始图片不同)。
正确的图像不那么吸引眼球,但绝对更好。

// scales the image by (float) scale < 1// returns a canvas containing the scaled image.function downScaleImage(img, scale) {    var imgCV = document.createElement('canvas');    imgCV.width = img.width;    imgCV.height = img.height;    var imgCtx = imgCV.getContext('2d');    imgCtx.drawImage(img, 0, 0);    return downScaleCanvas(imgCV, scale);}// scales the canvas by (float) scale < 1// returns a new canvas containing the scaled image.function downScaleCanvas(cv, scale) {    if (!(scale < 1) || !(scale > 0)) throw ('scale must be a positive number <1 ');    var sqScale = scale * scale; // square scale = area of source pixel within target    var sw = cv.width; // source image width    var sh = cv.height; // source image height    var tw = Math.floor(sw * scale); // target image width    var th = Math.floor(sh * scale); // target image height    var sx = 0, sy = 0, sIndex = 0; // source x,y, index within source array    var tx = 0, ty = 0, yIndex = 0, tIndex = 0; // target x,y, x,y index within target array    var tX = 0, tY = 0; // rounded tx, ty    var w = 0, nw = 0, wx = 0, nwx = 0, wy = 0, nwy = 0; // weight / next weight x / y    // weight is weight of current source point within target.    // next weight is weight of current source point within next target's point.    var crossX = false; // does scaled px cross its current px right border ?    var crossY = false; // does scaled px cross its current px bottom border ?    var sBuffer = cv.getContext('2d').    getImageData(0, 0, sw, sh).data; // source buffer 8 bit rgba    var tBuffer = new Float32Array(3 * tw * th); // target buffer Float32 rgb    var sR = 0, sG = 0,  sB = 0; // source's current point r,g,b            for (sy = 0; sy < sh; sy++) {        ty = sy * scale; // y src position within target        tY = 0 | ty;     // rounded : target pixel's y        yIndex = 3 * tY * tw;  // line index within target array        crossY = (tY != (0 | ty + scale));         if (crossY) { // if pixel is crossing botton target pixel wy = (tY + 1 - ty); // weight of point within target pixel nwy = (ty + scale - tY - 1); // ... within y+1 target pixel        }        for (sx = 0; sx < sw; sx++, sIndex += 4) { tx = sx * scale; // x src position within target tX = 0 |  tx;    // rounded : target pixel's x tIndex = yIndex + tX * 3; // target pixel index within target array crossX = (tX != (0 | tx + scale)); if (crossX) { // if pixel is crossing target pixel's right     wx = (tX + 1 - tx); // weight of point within target pixel     nwx = (tx + scale - tX - 1); // ... within x+1 target pixel } sR = sBuffer[sIndex    ];   // retrieving r,g,b for curr src px. sG = sBuffer[sIndex + 1]; sB = sBuffer[sIndex + 2];  if (!crossX && !crossY) { // pixel does not cross     // just add components weighted by squared scale.     tBuffer[tIndex    ] += sR * sqScale;     tBuffer[tIndex + 1] += sG * sqScale;     tBuffer[tIndex + 2] += sB * sqScale; } else if (crossX && !crossY) { // cross on X only     w = wx * scale;     // add weighted component for current px     tBuffer[tIndex    ] += sR * w;     tBuffer[tIndex + 1] += sG * w;     tBuffer[tIndex + 2] += sB * w;     // add weighted component for next (tX+1) px          nw = nwx * scale     tBuffer[tIndex + 3] += sR * nw;     tBuffer[tIndex + 4] += sG * nw;     tBuffer[tIndex + 5] += sB * nw; } else if (crossY && !crossX) { // cross on Y only     w = wy * scale;     // add weighted component for current px     tBuffer[tIndex    ] += sR * w;     tBuffer[tIndex + 1] += sG * w;     tBuffer[tIndex + 2] += sB * w;     // add weighted component for next (tY+1) px          nw = nwy * scale     tBuffer[tIndex + 3 * tw    ] += sR * nw;     tBuffer[tIndex + 3 * tw + 1] += sG * nw;     tBuffer[tIndex + 3 * tw + 2] += sB * nw; } else { // crosses both x and y : four target points involved     // add weighted component for current px     w = wx * wy;     tBuffer[tIndex    ] += sR * w;     tBuffer[tIndex + 1] += sG * w;     tBuffer[tIndex + 2] += sB * w;     // for tX + 1; tY px     nw = nwx * wy;     tBuffer[tIndex + 3] += sR * nw;     tBuffer[tIndex + 4] += sG * nw;     tBuffer[tIndex + 5] += sB * nw;     // for tX ; tY + 1 px     nw = wx * nwy;     tBuffer[tIndex + 3 * tw    ] += sR * nw;     tBuffer[tIndex + 3 * tw + 1] += sG * nw;     tBuffer[tIndex + 3 * tw + 2] += sB * nw;     // for tX + 1 ; tY +1 px     nw = nwx * nwy;     tBuffer[tIndex + 3 * tw + 3] += sR * nw;     tBuffer[tIndex + 3 * tw + 4] += sG * nw;     tBuffer[tIndex + 3 * tw + 5] += sB * nw; }        } // end for sx     } // end for sy    // create result canvas    var resCV = document.createElement('canvas');    resCV.width = tw;    resCV.height = th;    var resCtx = resCV.getContext('2d');    var imgRes = resCtx.getImageData(0, 0, tw, th);    var tByteBuffer = imgRes.data;    // convert float32 array into a UInt8Clamped Array    var pxIndex = 0; //      for (sIndex = 0, tIndex = 0; pxIndex < tw * th; sIndex += 3, tIndex += 4, pxIndex++) {        tByteBuffer[tIndex] = Math.ceil(tBuffer[sIndex]);        tByteBuffer[tIndex + 1] = Math.ceil(tBuffer[sIndex + 1]);        tByteBuffer[tIndex + 2] = Math.ceil(tBuffer[sIndex + 2]);        tByteBuffer[tIndex + 3] = 255;    }    // writing result to canvas.    resCtx.putImageData(imgRes, 0, 0);    return resCV;}

这是非常内存的贪婪,因为需要浮点缓冲区来存储目标图像的中间值(->如果我们计算结果画布,则在此算法中使用的是源图像的6倍内存)。
这也是非常昂贵的,因为无论目标大小如何都使用每个源像素,而且我们必须为getImageData / putImageDate付费,这也相当慢。
但是在这种情况下,没有比处理每个源值更快的方法了,情况也还不错:对于我的740 * 556袋熊的图像,处理时间在30到40毫秒之间。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/575342.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号