使用java提取图片的色彩边界

想做图片识别,但是大厂只提供接口,看不到实际代码,只能自己来动手了.这篇文章主要是介绍如何从一张图片里面提取色彩边界,用于后面做特征分析,算是基础部分.

图片处理步骤

  • 使用 Thumbnails 工具包将图片等比压缩到指定大小
  • 使用 JDK 自带的 BufferedImage 类灰度化图片
  • 通过判断一个像素和它相邻像素的颜色是否相近,来确定是不是色彩边界点,如果不是边界点,就把它设置为白色.
  • 除了图片最外围的一圈像素点之外,其他所有像素点依次执行第三步
  • 将处理结果打印到text文档中

引入jar包:

<!-- java图片工具 https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
    <groupId>net.coobird</groupId>
    <artifactId>thumbnailator</artifactId>
    <version>0.4.12</version>
</dependency>

实现代码如下:

import net.coobird.thumbnailator.Thumbnails;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;

public class ImageUtil {
    // 获取图片线条边界用
    // 阈值越大,空白越多
    // 值3000000适用于色彩分明的图片,如卡通人物蜡笔小新
    // 值1000000适用于色彩过渡平缓的图片,如3D卡通今年我们十七八岁
    private static final int threshold = 1000000;

    // 生成文本宽度
    // 值100适用于商标,头像等小图片
    // 值400适用于复杂,内容丰富的大图片
    private static final int picWidth = 400;

    public static void main(String[] args) throws IOException {
        String fromPic = "C:\\Users\\Public\\Pictures\\Sample Pictures\\241707553465767.png";
        String toTxt = "d:\\test5.txt";
        BufferedImage bufferedImage = ImageIO.read(new File(fromPic));
        // 1.压缩图片
        BufferedImage compactImage = Thumbnails.of(bufferedImage).size(picWidth, 1000).asBufferedImage();
        // 2.灰度化
        BufferedImage grayImage = grayingImage(compactImage);
        // 3.二值化
        //BufferedImage binaryImage = binaryImage(grayImage);

        // 4获取边界
        BufferedImage borderImage = getImageBorder(grayImage);

        // 5.输出到txt文本
        writeToTxt(borderImage, toTxt);

        // 6.保存图片
        File newFile = new File("d:\\test8.jpg");
        ImageIO.write(borderImage, "jpg", newFile);
    }

    /**
     * 灰度化图片
     * @param bufferedImage 原图片
     * @return 灰度化之后的图片
     */
    private static BufferedImage grayingImage(BufferedImage bufferedImage) {
        BufferedImage grayImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(),
                BufferedImage.TYPE_BYTE_GRAY);
        for (int i = 0; i < bufferedImage.getWidth(); i++) {
            for (int j = 0; j < bufferedImage.getHeight(); j++) {
                int color = bufferedImage.getRGB(i, j);
                grayImage.setRGB(i, j, color);
            }
        }
        return grayImage;
    }

    /**
     * 二值化图片
     * @param bufferedImage 原图片
     * @return 二值化后的图片
     */
    private static BufferedImage binaryImage(BufferedImage bufferedImage) {
        BufferedImage grayImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), bufferedImage.getType());
        int threshold = getMeanThreshold(bufferedImage);
        for (int i = 0; i < bufferedImage.getWidth(); i++) {
            for (int j = 0; j < bufferedImage.getHeight(); j++) {
                int color = bufferedImage.getRGB(i, j);
                int r = (color >> 16) & 0xff;
                int g = (color >> 8) & 0xff;
                int b = color & 0xff;
                int gray = (int) (0.3 * r + 0.59 * g + 0.11 * b);
                if (gray > threshold) {
                    // 白色
                    grayImage.setRGB(i, j, 0xFFFFFF);
                }
                else {
                    // 黑色
                    grayImage.setRGB(i, j, 0);
                }
            }
        }
        return grayImage;
    }

    /**
     * 获取图片的阀值,采用基于灰度平均值的阈值
     * @param bufferedImage 原图片
     * @return 二值化的阈值
     */
    private static int getMeanThreshold(BufferedImage bufferedImage) {
        int w = bufferedImage.getWidth();
        int h = bufferedImage.getHeight();
        int num = 0;
        int sum = 0;
        for(int i=0; i<w; i++) {
            for(int j = 0; j < h; j++) {
                int color = bufferedImage.getRGB(i, j);
                int r = (color >> 16) & 0xff;
                int g = (color >> 8) & 0xff;
                int b = color & 0xff;
                int gray = (int) (0.3 * r + 0.59 * g + 0.11 * b);
                sum += gray;
                num += 1;
            }
        }

        // 测试表明,阀值取平均值的1.2倍效果最好。
        int threshold = sum / num;
        if(threshold * 1.2 < 255) {
            threshold = (int)(1.2 * sum / num);
        }
        System.out.println("width: " + w + " height: " + h + " threshold: " + threshold);
        return threshold;
    }

    /**
     * 输出 0,1 TXT文本
     */
    public static void writeToTxt(BufferedImage bufferedImage, String toSaveFilePath) {
        File file = new File(toSaveFilePath);
        try {
            Writer writer = new OutputStreamWriter(new FileOutputStream(file, true), StandardCharsets.UTF_8);
            StringBuilder builder = new StringBuilder();
            for (int j = 0; j < bufferedImage.getHeight(); j++) {
                for(int i = 0; i < bufferedImage.getWidth(); i++) {
                    int color = bufferedImage.getRGB(i, j);
                    if(color == -1) {
                        builder.append("  ");
                    }
                    else {
                        builder.append("0 ");
                    }
                }
                builder.append("\r\n");
            }
            writer.write(builder.toString());
            writer.close();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 提取二值化后图片的边界
     * 对二维码有奇效
     * @param bufferedImage 原图片
     * @return 二值化后的图片
     */
    private static BufferedImage getImageBorder(BufferedImage bufferedImage) {
        BufferedImage borderImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), bufferedImage.getType());
        //List<String> toDealPoints = new ArrayList<>();
        int imgWidth = bufferedImage.getWidth();
        int imgHeight = bufferedImage.getHeight();
        for (int i = 1; i < imgWidth - 1; i++) {
            for (int j = 1; j < imgHeight - 1; j++) {
                // 当前点
                int color = bufferedImage.getRGB(i, j);
                // 上点
                int upColor = bufferedImage.getRGB(i, j - 1);
                // 下点
                int downColor = bufferedImage.getRGB(i, j + 1);
                // 左点
                int leftColor = bufferedImage.getRGB(i - 1, j);
                // 右点
                int rightColor = bufferedImage.getRGB(i + 1, j);

                // 如果某个黑点的上下左右点都为黑点,就表示它不是边界,把它设为白点
                if(isQualified(color, upColor, downColor, leftColor, rightColor)) {
                    // 白色
                    borderImage.setRGB(i, j, 0xFFFFFF);
                }
                else {
                    // 原色不变
                    borderImage.setRGB(i, j, color);
                }
            }
        }

        return borderImage;
    }

    /**
     * 根据设置的阈值,判断当前点是否是边界点
     * 判断规则如下:
     * 如果当前点是白色的点,直接跳过
     * 如果当前点不是白色,且它的上下左右4个点和它的差别都在阈值内,
     * 那么就认为它不是边界点,返回true,否则返回false;
     * @param color 当前点
     * @param upColor 上点
     * @param downColor 下点
     * @param leftColor 左点
     * @param rightColor 右点
     * @return 是否设置为白色
     */
    public static boolean isQualified(int color, int upColor, int downColor, int leftColor, int rightColor) {
        // color == -1 表示白色,白色的不需要再设置为白色
        return color != -1 && (Math.abs(color - upColor) < threshold
                && Math.abs(color - downColor) < threshold
                && Math.abs(color - leftColor) < threshold
                && Math.abs(color - rightColor) < threshold);
    }

}

打印效果:
在这里插入图片描述

总结

  • 图片名字叫今年我们十七八岁,处理后的边界还是蛮清楚的
  • 对于噪点处理,后面还需要加上
  • 另外复杂的图片,可以划分为几块,不同的版块使用不同的阈值,不然有些区域的边界很清晰,有些就很模糊
  • 图片不大还可以整体遍历,后面如果需要处理的图片多,效率就不行了.
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
边界提取算法是通过对给定点集进行处理,筛选出位于点集边界上的点。在Java中,可以用以下步骤来实现边界提取算法: 1. 定义一个方法,命名为`extractBoundaryPoints`,该方法接收一个点集合作为参数,并返回一个边界点的集合。 2. 在`extractBoundaryPoints`方法中,创建一个空的边界点集合,用于存储筛选后的边界点。 3. 遍历点集合中的每个点,逐个进行判断是否为边界点。 4. 对于每个点,判断其是否满足边界点的条件。例如,可以判断一个点是否满足以下条件:它的x坐标或y坐标与点集合的最大或最小值相等。 5. 如果某个点满足边界点的条件,则将其添加到边界点集合中。 6. 遍历完整个点集合后,`extractBoundaryPoints`方法返回边界点集合。 7. 在主程序中,调用`extractBoundaryPoints`方法并传入一个点集合,然后打印输出返回的边界点集合。 下面是一个示例代码: ```java import java.util.ArrayList; import java.util.List; public class BoundaryPointsExtractor { public static List<Point> extractBoundaryPoints(List<Point> points) { List<Point> boundaryPoints = new ArrayList<>(); // 遍历点集合,判断是否为边界点 for (Point point : points) { if (isBoundaryPoint(point, points)) { boundaryPoints.add(point); } } return boundaryPoints; } private static boolean isBoundaryPoint(Point point, List<Point> points) { int maxX = Integer.MIN_VALUE; int minX = Integer.MAX_VALUE; int maxY = Integer.MIN_VALUE; int minY = Integer.MAX_VALUE; for (Point p : points) { maxX = Math.max(maxX, p.x); minX = Math.min(minX, p.x); maxY = Math.max(maxY, p.y); minY = Math.min(minY, p.y); } return point.x == maxX || point.x == minX || point.y == maxY || point.y == minY; } public static void main(String[] args) { List<Point> points = new ArrayList<>(); points.add(new Point(1, 2)); points.add(new Point(3, 4)); points.add(new Point(1, 5)); points.add(new Point(5, 2)); List<Point> boundaryPoints = extractBoundaryPoints(points); for (Point point : boundaryPoints) { System.out.println("(" + point.x + ", " + point.y + ")"); } } } class Point { public int x; public int y; public Point(int x, int y) { this.x = x; this.y = y; } } ``` 在示例代码中,定义了一个`BoundaryPointsExtractor`类,其中包含了两个静态方法:`extractBoundaryPoints`和`isBoundaryPoint`。`extractBoundaryPoints`方法用于提取边界点,`isBoundaryPoint`方法用于判断某个点是否为边界点。最后,在主程序中调用了`extractBoundaryPoints`方法,并打印输出了边界点的坐标。这个示例代码可以根据实际情况进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值