【问题标题】:Check if all pixels in a region are all empty in Javascript?检查一个区域中的所有像素在Javascript中是否都是空的?
【发布时间】:2015-03-17 02:35:44
【问题描述】:

我有一个 2D 画布,上面画了一些东西,我想知道一个区域中的所有像素 (rect - x,y,w,h) 是否都是空的/完全透明的?我知道这可以用 getImageData 完成,但有更快的方法吗?我正在编写一个简单的 java 脚本图像打包器,我希望从最终工作表中排除空图像。

【问题讨论】:

    标签: javascript canvas


    【解决方案1】:

    读取像素的唯一方法是使用getImageData(),但您可以通过使用与默认Uint8ClampedArray不同的视图来加速这种检查,例如Uint32Array,它允许您读取单个像素每次迭代:

    function isEmpty(ctx, x, y, w, h) {
    
        var idata = ctx.getImageData(x, y, w, h),      // needed as usual ...
            u32 = new Uint32Array(idata.data.buffer),  // reads 1x uint32 instead of 4x uint8
            i = 0, len = u32.length;
    
        while(i < len) if (u32[i++]) return false;     // if !== 0 return false, not empty
        return true                                    // all empty, all OK
    }
    

    但是,这不能用于检查透明度。即使一个像素是完全透明的,其他通道中也可能存在颜色数据。例如,这会产生一个不可见的像素:rgba(255,128,0,0)isEmpty() 会报告该区域是非空的,即使该像素不可见。

    要检查这些情况,您只需要检查 Alpha 通道,您可以简单地修改上面的内容以使用 AND 掩码来过滤颜色数据,或者将 Alpha 通道位移过来,将其他位推出- 无论哪种情况,我们都追求非 0 值。

    由于这是小端 (LSB) 格式(在当今大多数主流计算机上),组件的顺序为 ABGR (0xAABBGGRR),因此我们可以:

    u32[i] & 0xff000000
    

    或使用移位(符号在这种情况下并不重要,但我个人更喜欢在处理无符号数字时使用无符号移位(>>> 而不是 >>)):

    u32[i]>>>24
    

    性能方面差别很小,如果有的话,我猜 ANDing 会稍微快一点:

    function isTransparent(ctx, x, y, w, h) {
    
        var idata = ctx.getImageData(x, y, w, h),       // needed as usual ...
            u32 = new Uint32Array(idata.data.buffer),   // reads 1x uint32 instead of 4x bytes
            i = 0, len = u32.length;
    
        while(i < len) if (u32[i++] & 0xff000000) return false; // not transparent?
        return true                                     // all transparent, all OK
    }
    

    位移

    function isTransparent(ctx, x, y, w, h) {
    
        var idata = ctx.getImageData(x, y, w, h),       // needed as usual ...
            u32 = new Uint32Array(idata.data.buffer),   // reads 1x uint32 instead of 4x bytes
            i = 0, len = u32.length;
    
        while(i < len) if (u32[i++]>>>24) return false; // not transparent?
        return true                                     // all transparent, all OK
    }
    

    更新:

    加速技巧

    如果您知道要检查的数据至少有一定大小,比如说 2x2 像素,您还可以通过跳过每隔一个像素,甚至每隔一行来提高速度:

    while(i < len) if (u32[(i += 2)]>>>24) return false; // skips every 2. pixel
    

    对于行,你需要两个迭代器:

    while(i < len) {
        var endLine = i + width, p = i;  // p in case you deal with odd widths
        while(p < endLine) if (u32[(p += 2)]>>>24) return false; // skip every 2. pixel
        i += width * 2;  // skip a line
    }
    

    【讨论】:

    • 为好答案点赞。鉴于随机精灵可能在图像中“居中”,您的答案可能会通过首先测试最有可能被填充(不透明)的像素的小样本来改进。在这第一遍中,测试矩形的中心点像素并以“X”模式向外测试。这可能会比从左上角的像素开始更快地找到一个不透明的像素。
    • @markE 我同意提供图像大小的逻辑,但我不确定增加简单问题的复杂性是否会使其运行得更快,即。添加了迭代器、边界检查、X 的一些模式机制(如何在两者之间检查)等。如果已知数据至少为 2x2,我认为跳过每隔一个像素或类似的东西会更好。但如果你有一个可行的算法,我很高兴被证明是错误的。
    猜你喜欢
    • 2012-04-27
    • 1970-01-01
    • 2019-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 2020-06-25
    • 2018-12-13
    相关资源
    最近更新 更多