Codename One中如何自动裁剪图像

3
我需要在Codename One中自动裁剪图像。 自动裁剪图像是指从该图像中删除边框。以下代码的目的是搜索尽可能大的边框区域,即所有相同颜色(或透明度)的区域,然后将此区域从图像中裁剪。
为了更好地理解我编写的算法,请考虑以下图片(注意topx、topy、bottomx、bottomy): enter image description here 这是我的代码。你能帮我理解哪里出错了并修复它吗?正如日志和屏幕截图所报告的那样,它没有按预期工作。
Image arrowDownIcon = FontImage.createMaterial(FontImage.MATERIAL_ARROW_DROP_DOWN, "Color-Gray", 30);
Form hi = new Form("Hi World", new FlowLayout(Component.CENTER));
hi.getToolbar().hideToolbar();
hi.getContentPane().setUIID("NoMarginNoPadding");
hi.add(new Label(getAutoCroppedImage(arrowDownIcon), "NoMarginNoPadding"));
hi.show();

CSS

#Constants {
    includeNativeBool: true; 
}

NoMarginNoPadding {
    margin: 0px;
    padding: 0px;
    border: 1pt blue solid;
}

    /**
     * Autocrop an image, using as base color the pixel at top left
     *
     * @param source
     * @return
     */
    public static Image getAutoCroppedImage(Image source) {
        if (source == null) {
            throw new IllegalArgumentException("ImageUtilities.getCroppedImage -> null source image");
        }
        if (source instanceof FontImage) {
            source = ((FontImage) source).toImage();
        }

        int[] pixels = source.getRGB(); // array instance containing the ARGB data within this image

        // Get top-left pixel color as "baseline" for cropping (it can be any color or transparent)
        int baseColor = pixels[0];

        int width = source.getWidth();
        int height = source.getHeight();

        int topy = 0;
        int topx = 0;
        int bottomy = height - 1;
        int bottomx = width - 1;

        // Search for topy, iterating the pixels from top to bottom
        for (int y = 0; y < height && topy == 0; y++) {
            for (int x = 0; x < width; x++) {
                if (pixels[y * width + x] == baseColor) {
                    topy = y;
                    break;
                }
            }
        }

        // Search for topx, interating the pixels from left to right
        for (int x = 0; x < width && topx == 0; x++) {
            for (int y = 0; y < height; y++) {
                if (pixels[y * width + x] == baseColor) {
                    topx = x;
                    break;
                }
            }
        }

        // Search for bottomy, iterating from bottom to top
        for (int y = height - 1; y >= 0 && bottomy == height - 1; y--) {
            for (int x = 0; x < width; x++) {
                if (pixels[y * width + x] == baseColor) {
                    bottomy = y;
                    break;
                }
            }
        }

        // Search for bottomx, interating from right to left
        for (int x = width - 1; x >= 0 && bottomx == width - 1; x--) {
            for (int y = 0; y < height; y++) {
                if (pixels[y * width + x] == baseColor) {
                    bottomx = x;
                    break;
                }
            }
        }

        Image destination = Image.createImage((bottomx - topx), (bottomy - topy), 0);

        Log.p("Original width: " + width, Log.DEBUG);
        Log.p("Original height: " + height, Log.DEBUG);
        Log.p("Cropped width: " + destination.getWidth(), Log.DEBUG);
        Log.p("Cropped height: " + destination.getHeight(), Log.DEBUG);
        Log.p("Top-left point cropped image: (" + topx + "," + topy + ")", Log.DEBUG);
        Log.p("Bottom-right point cropped image: (" + bottomx + "," + bottomy + ")", Log.DEBUG);

        Graphics graphics = destination.getGraphics();
        graphics.drawImage(source, topx, topy, destination.getWidth(), destination.getHeight());

        return destination;
    }

日志:

[EDT] 0:0:0,120 - Original width: 529
[EDT] 0:0:0,120 - Original height: 529
[EDT] 0:0:0,120 - Cropped width: 526
[EDT] 0:0:0,120 - Cropped height: 526
[EDT] 0:0:0,120 - Top-left point cropped image: (1,1)
[EDT] 0:0:0,120 - Bottom-right point cropped image: (527,527)

截图:

在此输入图片描述

2个回答

1
你对基准颜色的期望与图片工作的实际情况不符。对于JPEG格式尤其如此,它可以有各种“白”色,并且您将需要一个阈值。但是这也在某种程度上适用于无损图像格式(如PNG),其中像alpha这样的值可能会表现“奇怪”,例如,有效的PNG文件可以返回0x00ffffff和0x0两个像素,仍然是有效的!
这种情况发生在绘图应用程序具有一些透明内容时,大多数艺术家无法区分,而应用程序也不会丢弃那些不可见的数据。因此,您需要添加显式的alpha测试,例如:
if ((pixels[y * width + x] == baseColor || (pixels[y * width + x] & 0xff000000) == 0) { 
     ... 
}

谢谢Shai,你给了我正确的提示。我的代码还有其他错误:我添加了一个新答案,完全修复了我的代码,希望它对其他人有用。 - Francesco Galgani

1

Shai的回答 给了我正确的提示。然而我的代码还有另外两个错误:第一个是比较应该是 color != baseColor 而不是 color == baseColor;第二个是绘制新图像的参数应该是 graphics.drawImage(source, -topx, -topy); 而不是我写的那些。

在我的使用情况中,阈值并不是必要的,但对于任何想要实现它们的人,我建议使用 Codename One 的 ColorUtil API 而不是位比较器来获取 alpha、red、green 和 blue,因为该 API 比位比较器更易读和直观。

这是我修复后的方法,我还添加了一个截屏以演示它的工作原理:

    /**
     * Autocrop an image, using as base color the pixel at top left
     *
     * @param source
     * @return
     */
    public static Image getAutoCroppedImage(Image source) {
        if (source == null) {
            throw new IllegalArgumentException("ImageUtilities.getCroppedImage -> null source image");
        }
        if (source instanceof FontImage) {
            source = ((FontImage) source).toImage();
        }

        int[] pixels = source.getRGB(); // array instance containing the ARGB data within this image

        // Get top-left pixel color as "baseline" for cropping (it can be any color or transparent)
        int baseColor = pixels[0];

        int width = source.getWidth();
        int height = source.getHeight();

        int topy = 0;
        int topx = 0;
        int bottomy = height - 1;
        int bottomx = width - 1;

        // Search for topy, iterating the pixels from top to bottom
        for (int y = 0; y < height && topy == 0; y++) {
            for (int x = 0; x < width; x++) {
                int color = pixels[y * width + x];
                int alpha = ColorUtil.alpha(color);
                if (color != baseColor && alpha != 0) {
                    topy = y;
                    break;
                }
            }
        }

        // Search for topx, interating the pixels from left to right
        for (int x = 0; x < width && topx == 0; x++) {
            for (int y = 0; y < height; y++) {
                int color = pixels[y * width + x];
                int alpha = ColorUtil.alpha(color);
                if (color != baseColor && alpha != 0) {
                    topx = x;
                    break;
                }
            }
        }

        // Search for bottomy, iterating from bottom to top
        for (int y = height - 1; y >= 0 && bottomy == height - 1; y--) {
            for (int x = 0; x < width; x++) {
                int color = pixels[y * width + x];
                int alpha = ColorUtil.alpha(color);
                if (color != baseColor && alpha != 0) {
                    bottomy = y;
                    break;
                }
            }
        }

        // Search for bottomx, interating from right to left
        for (int x = width - 1; x >= 0 && bottomx == width - 1; x--) {
            for (int y = 0; y < height; y++) {
                int color = pixels[y * width + x];
                int alpha = ColorUtil.alpha(color);
                if (color != baseColor && alpha != 0) {
                    bottomx = x;
                    break;
                }
            }
        }

        Image destination = Image.createImage((bottomx - topx), (bottomy - topy), 0);

        Log.p("Original width: " + width, Log.DEBUG);
        Log.p("Original height: " + height, Log.DEBUG);
        Log.p("Cropped width: " + destination.getWidth(), Log.DEBUG);
        Log.p("Cropped height: " + destination.getHeight(), Log.DEBUG);
        Log.p("Top-left point cropped image: (" + topx + "," + topy + ")", Log.DEBUG);
        Log.p("Bottom-right point cropped image: (" + bottomx + "," + bottomy + ")", Log.DEBUG);

        Graphics graphics = destination.getGraphics();
        graphics.drawImage(source, -topx, -topy);

        return destination;
    }

屏幕截图:

Autocrop an image in Codename One


我编辑了我的回答,修复了另一个错误。现在应该没问题了。 - Francesco Galgani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接