裁剪图像的透明边缘

Posted

技术标签:

【中文标题】裁剪图像的透明边缘【英文标题】:Crop transparent edges of an image 【发布时间】:2017-11-07 18:13:18 【问题描述】:

我正在尝试制作一个仅从右侧和左侧裁剪BufferedImage 的透明边缘的函数。

所以,我在网上的某个地方找到了这种方法,但是我真的不明白pixels[] 数组填充了什么?我假设它是每个像素的 alpha、红色、绿色、蓝色值(根据 (j*width+i)*4 中的 4 判断,它每第四个元素选取一次),但是对于 @987654326,这个数组的长度是 299208 @image(我知道它不是 2 的幂,忽略它)。

那么,我该如何做到这一点,我很好奇这个像素阵列中实际存储了什么?

public static BufferedImage trimHorizontally(BufferedImage img) 
    final byte[] pixels = ((DataBufferByte)img.getRaster().getDataBuffer()).getData();
    int width = img.getWidth(), height = img.getHeight();
    int x0, x1;
    int j, i;

    leftLoop:
        for(i = 0; i < width; i++) 

            for(j = 0; j < height; j++) 

                if(pixels[(j*width+i)*4] != 0) 
                    break leftLoop;
                
            
        
    x0 = i;
    rightLoop:
        for(i = width-1; i >= 0; i--) 

            for(j = 0; j < height; j++) 

                if(pixels[(j*width+i)*4] != 0) 
                    break rightLoop;
                
            
        
    x1 = i+1;

    return img.getSubimage(x0, 0, x1-x0, height);



我在上面的代码中基本上做的是从左侧和右侧扫描图像,检查 alpha 值并标记我需要裁剪的距离,但它会引发异常,因为循环完成时没有检测任何不透明的像素(我裁剪的图像肯定不透明)。

我查了一下,发现大于0的alpha值实际上在pixels[]数组的35000的索引之上,通过(j*width+i)*4的计算我只能达到18000。 ...

【问题讨论】:

pixels[] 数组确实存储了您的想法。不知道为什么长度这么长。 您的图像是否已经使用getSubimage(...) 方法裁剪?请注意,此方法不会创建副本,因此图像将与原始图像共享支持数组(包括大小)。您可以从Raster 获得正确的偏移量/尺寸。此外,将DataBuffer 无条件转换为DataBufferIntDataBufferByte 显然是不安全的,除非您完全控制输入图像。 是的,图像在使用此方法之前已使用 getSubimage() 进行裁剪... 【参考方案1】:

快速修复

您可以使用以下方法制作像素:

final int[] pixels = ((DataBufferInt)img.getAlphaRaster().getDataBuffer()).getData();

并删除两个(j*width+i)*4 中的*4。这只会使您的 pixels[] 仅包含 alpha 值,而不包含 RGB。

但是有一个更安全(没有不安全的DataBuffer 强制转换)、更易读的解决方案:

首选版本

我所做的更改是停止使用可怕的byte 数组,而只需使用getRGB() 方法。 alpha值可以通过使用the RGB+alpha constructor创建Color,然后使用getAlpha()方法获得。

现在下面的代码对我有用:

public static BufferedImage trimHorizontally(BufferedImage img) 
    int width = img.getWidth(), height = img.getHeight();
    int x0, x1;
    int j, i;
    int alpha;

    leftLoop:
        for(i = 0; i < width; i++) 

            for(j = 0; j < height; j++) 
                if(new Color(img.getRGB(i, j), true).getAlpha() != 0)  
                    break leftLoop;
                
            
        
    x0 = i;
    rightLoop:
        for(i = width-1; i >= 0; i--) 

            for(j = 0; j < height; j++) 
                if(new Color(img.getRGB(i, j), true).getAlpha() != 0) 
                    break rightLoop;
                
            
        
    x1 = i+1;

    return img.getSubimage(x0, 0, x1-x0, height);

注意事项:

img 必须是TYPE_INT_ARGB 类型,或者是在getRGB() 数据的最后一个字节中保留其alpha 值的其他类型(如果您不知道这意味着什么,只需尝试上面的代码如果它不起作用covert to ARGB。)

还要确保使用image format that supports transparency(不是 JPG)。

【讨论】:

是的,确实如此。但是,我在某处听说使用 getRGB 与使用像素 [] 数组的方式相比效率低下。

以上是关于裁剪图像的透明边缘的主要内容,如果未能解决你的问题,请参考以下文章

显示时使图像边缘透明

如何裁剪图像边缘

如何裁剪仅在透明区域显示的图像?

Imagemagick 将透明图像裁剪为蒙版

C#裁剪图像中的透明部分

裁剪掉透明像素但保留偏移量