在代号一中自动裁剪图像
Autocrop an image in Codename One
我需要在代号一中自动裁剪图像。
自动裁剪图像意味着从该图像中删除边框。以下代码的目的是搜索最大可能的全部相同颜色(或透明度)的边框区域,然后从图像中裁剪该区域。
为了更好地理解我写的算法,请看下图(注意谁是topx, topy, bottomx, bottomy):
这是我的代码。您能帮我了解问题所在并解决吗?正如日志和屏幕截图所报告的那样,它没有按预期工作:
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)
截图:
您对基线颜色的期望与图像工作方式的实际情况不符。对于 JPEG 尤其如此,它可以具有多种 "white" 颜色并且您需要一个阈值。但在某种程度上,对于像 PNG 这样的无损图像格式也是如此,其中像 alpha 这样的值可以表现 "oddly" 例如一个有效的 PNG 文件可以 return 0x00ffffff 和 0x0 两个像素并且仍然有效!
当绘图应用程序有一些透明内容时会发生这种情况,大多数艺术家无法分辨其中的区别并且应用程序不会丢弃那些不可见的数据。因此,您需要为 alpha 添加显式测试,例如:
if ((pixels[y * width + x] == baseColor || (pixels[y * width + x] & 0xff000000) == 0) {
...
}
给了我正确的提示。但是我的代码还有另外两个错误:第一个是比较应该是 color != baseColor
而不是 color == baseColor
;第二个是绘制新图像的参数应该是 graphics.drawImage(source, -topx, -topy);
而不是我写的参数。
在我的用例中,阈值不是必需的,但是对于任何想要实现它们的人,我建议使用代号一 ColorUtil
API 而不是按位比较器来获得 alpha ,红色,绿色和蓝色,因为 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;
}
截图:
我需要在代号一中自动裁剪图像。 自动裁剪图像意味着从该图像中删除边框。以下代码的目的是搜索最大可能的全部相同颜色(或透明度)的边框区域,然后从图像中裁剪该区域。
为了更好地理解我写的算法,请看下图(注意谁是topx, topy, bottomx, bottomy):
这是我的代码。您能帮我了解问题所在并解决吗?正如日志和屏幕截图所报告的那样,它没有按预期工作:
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)
截图:
您对基线颜色的期望与图像工作方式的实际情况不符。对于 JPEG 尤其如此,它可以具有多种 "white" 颜色并且您需要一个阈值。但在某种程度上,对于像 PNG 这样的无损图像格式也是如此,其中像 alpha 这样的值可以表现 "oddly" 例如一个有效的 PNG 文件可以 return 0x00ffffff 和 0x0 两个像素并且仍然有效!
当绘图应用程序有一些透明内容时会发生这种情况,大多数艺术家无法分辨其中的区别并且应用程序不会丢弃那些不可见的数据。因此,您需要为 alpha 添加显式测试,例如:
if ((pixels[y * width + x] == baseColor || (pixels[y * width + x] & 0xff000000) == 0) {
...
}
color != baseColor
而不是 color == baseColor
;第二个是绘制新图像的参数应该是 graphics.drawImage(source, -topx, -topy);
而不是我写的参数。
在我的用例中,阈值不是必需的,但是对于任何想要实现它们的人,我建议使用代号一 ColorUtil
API 而不是按位比较器来获得 alpha ,红色,绿色和蓝色,因为 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;
}
截图: