opencv 图片颜色+轮廓识别

目标图片:

  • 1 简单识别图片中出现颜色最多的
java 复制代码
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class SimpleImageColorRecognizer implements ImageColorRecognizer {

    @Override
    public int recognizeDominantColor(String imagePath) throws IOException {
        File file = new File(imagePath);
        BufferedImage image = ImageIO.read(file);
        return recognizeDominantColor(image);
    }

    @Override
    public int recognizeDominantColor(BufferedImage image) {
        int width = image.getWidth();
        int height = image.getHeight();
        int[] colorCount = new int[256 * 256 * 256]; // RGB颜色空间

        // 遍历图片的每个像素
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int rgb = image.getRGB(x, y);
                colorCount[rgb & 0x00FFFFFF]++; // 忽略Alpha通道
            }
        }

        // 找到出现次数最多的颜色
        int maxCount = -1;
        int dominantColor = 0;
        for (int i = 0; i < colorCount.length; i++) {
            if (colorCount[i] > maxCount) {
                maxCount = colorCount[i];
                dominantColor = i;
            }
        }

        return dominantColor;
    }

    public static void main(String[] args) {
        try {
            ImageColorRecognizer recognizer = new SimpleImageColorRecognizer();
            int dominantColor = recognizer.recognizeDominantColor("D:\\tmp\\b1.png");
            Color color = new Color(dominantColor);
            System.out.println("Dominant Color: R=" + color.getRed() + " G=" + color.getGreen() + " B=" + color.getBlue());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 2、按颜色深浅输出多组颜色
java 复制代码
public class MultiColorRecognizer {
    /**
     * 使用K-Means聚类算法识别图片中的多种颜色
     *
     * @param imagePath 图片路径
     * @param k         聚类的数量(颜色的数量)
     * @return 返回聚类后的颜色列表(RGB值)
     * @throws IOException 如果图片无法读取
     */
    public static List<Color> recognizeColors(String imagePath, int k) throws IOException {
        File file = new File(imagePath);
        BufferedImage image = ImageIO.read(file);
        return recognizeColors(image, k);
    }

    /**
     * 使用K-Means聚类算法识别图片中的多种颜色
     *
     * @param image BufferedImage对象
     * @param k     聚类的数量(颜色的数量)
     * @return 返回聚类后的颜色列表(RGB值)
     */
    public static List<Color> recognizeColors(BufferedImage image, int k) {
        // 提取图片中的所有像素颜色
        List<int[]> pixels = new ArrayList<>();
        int width = image.getWidth();
        int height = image.getHeight();
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int rgb = image.getRGB(x, y);
                int r = (rgb >> 16) & 0xFF;
                int g = (rgb >> 8) & 0xFF;
                int b = rgb & 0xFF;
                pixels.add(new int[]{r, g, b});
            }
        }
        // 使用K-Means聚类
        KMeans kmeans = new KMeans(k, pixels);
        List<int[]> centroids = kmeans.run();

        // 将聚类中心转换为Color对象
        List<Color> colors = new ArrayList<>();
        for (int[] centroid : centroids) {
            int r = centroid[0];
            int g = centroid[1];
            int b = centroid[2];
            colors.add(new Color(r, g, b));
        }

        return colors;
    }

    public static void main(String[] args) {
        try {
            String imagePath = "D:\\tmp\\b1.png";
            int k = 5; // 聚类的数量(提取5种主要颜色)
            List<Color> colors = recognizeColors(imagePath, k);
            // 输出提取的颜色
            System.out.println("Extracted Colors:");
            for (Color color : colors) {
                System.out.println("R=" + color.getRed() + " G=" + color.getGreen() + " B=" + color.getBlue());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 3、颜色+轮廓(依赖opencv_java4110)
java 复制代码
public class ColorShapeRecognizer {
    static {
        // 直接加载特定路径下的 DLL 文件
        System.load("D:\\opencv4.11\\build\\java\\x64\\opencv_java4110.dll");
    }

    /**
     * 识别图片中的颜色及其形状
     *
     * @param imagePath 图片路径
     * @param k         聚类的数量(颜色的数量)
     * @return 返回颜色、总轮廓面积和轮廓信息的列表
     * @throws IOException 如果图片无法读取
     */
    public static List<ColorShapeArea> recognizeColorsAndShapes(String imagePath, int k) throws IOException {
        // 读取图片
        Mat image = Imgcodecs.imread(imagePath);
        if (image.empty()) {
            throw new IOException("无法读取图片: " + imagePath);
        }
        // 将图片转换为RGB格式
        Mat rgbImage = new Mat();
        Imgproc.cvtColor(image, rgbImage, Imgproc.COLOR_BGR2RGB);
        // 提取图片中的所有像素颜色
        List<int[]> pixels = new ArrayList<>();
        for (int y = 0; y < rgbImage.rows(); y++) {
            for (int x = 0; x < rgbImage.cols(); x++) {
                double[] pixel = rgbImage.get(y, x);
                int r = (int) pixel[0];
                int g = (int) pixel[1];
                int b = (int) pixel[2];
                pixels.add(new int[]{r, g, b});
            }
        }
        // 使用K-Means聚类
        KMeans kmeans = new KMeans(k, pixels);
        List<int[]> centroids = kmeans.run();
        // 存储颜色、总轮廓面积和轮廓信息
        List<ColorShapeArea> colorShapeAreas = new ArrayList<>();
        // 为每种颜色生成掩码并检测形状
        Mat contourImage = image.clone(); // 复制原图用于绘制轮廓
        for (int i = 0; i < centroids.size(); i++) {
            int[] centroid = centroids.get(i);
            int r = centroid[0];
            int g = centroid[1];
            int b = centroid[2];
            // 定义颜色范围(动态范围,增加容错范围)
            Scalar lowerBound = new Scalar(Math.max(r - 30, 0), Math.max(g - 30, 0), Math.max(b - 30, 0));
            Scalar upperBound = new Scalar(Math.min(r + 30, 255), Math.min(g + 30, 255), Math.min(b + 30, 255));
            // 生成掩码
            Mat mask = new Mat();
            Core.inRange(rgbImage, lowerBound, upperBound, mask);
            // 形态学操作:闭运算去除噪声并连接断开的部分
            Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(7, 7));
            Imgproc.morphologyEx(mask, mask, Imgproc.MORPH_CLOSE, kernel);
            // 检测轮廓
            List<MatOfPoint> contours = new ArrayList<>();
            Mat hierarchy = new Mat();
            Imgproc.findContours(mask, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
            // 计算总轮廓面积
            double totalArea = 0;
            List<ContourInfo> contourInfos = new ArrayList<>();
            for (MatOfPoint contour : contours) {
                double contourArea = Imgproc.contourArea(contour);
                totalArea += contourArea;
                // 识别形状
                ShapeInfo shapeInfo = recognizeShape(contour);
                // 存储轮廓信息
                contourInfos.add(new ContourInfo(contourArea, shapeInfo));
            }
            // 存储颜色、总轮廓面积和轮廓信息
            colorShapeAreas.add(new ColorShapeArea(new Color(r, g, b), totalArea, contourInfos));
            // 在原图上绘制轮廓
            Imgproc.drawContours(contourImage, contours, -1, new Scalar(r, g, b), 2);
        }
        // 按总轮廓面积从大到小排序
        Collections.sort(colorShapeAreas, Comparator.comparingDouble(ColorShapeArea::getTotalArea).reversed());
        // 输出排序后的颜色、总轮廓面积和轮廓信息
        for (ColorShapeArea colorShapeArea : colorShapeAreas) {
            Color color = colorShapeArea.getColor();
            double totalArea = colorShapeArea.getTotalArea();
            List<ContourInfo> contourInfos = colorShapeArea.getContourInfos();
            System.out.println("Color: R=" + color.getRed() + " G=" + color.getGreen() + " B=" + color.getBlue() +
                    " - Total Area: " + totalArea +
                    " - Contour Count: " + contourInfos.size());
            for (ContourInfo contourInfo : contourInfos) {
                double contourArea = contourInfo.getContourArea();
                ShapeInfo shapeInfo = contourInfo.getShapeInfo();
                System.out.println("  Contour Area: " + contourArea +
                        " - Shape: " + shapeInfo.getShape() +
                        " - Parameters: " + shapeInfo.getParameters());
            }
        }
        // 保存结果图像(包含轮廓)
        String outputPath = "D:\\tmp\\contour_image" + System.currentTimeMillis() + ".png";
        Imgcodecs.imwrite(outputPath, contourImage);
        System.out.println("轮廓图像已保存至: " + outputPath);
        // 返回颜色、总轮廓面积和轮廓信息的列表
        return colorShapeAreas;
    }

    /**
     * 识别轮廓形状
     *
     * @param contour 轮廓
     * @return 形状信息(形状名称和参数)
     */
    private static ShapeInfo recognizeShape(MatOfPoint contour) {
        String shape = "未知";
        List<String> parameters = new ArrayList<>();
        MatOfPoint2f contour2f = new MatOfPoint2f(contour.toArray());
        double peri = Imgproc.arcLength(contour2f, true);
        MatOfPoint2f approx = new MatOfPoint2f();
        Imgproc.approxPolyDP(contour2f, approx, 0.04 * peri, true);
        if (approx.total() == 3) {
            shape = "三角形";
        } else if (approx.total() == 4) {
            // 计算边界框
            Rect boundingRect = Imgproc.boundingRect(contour);
            double aspectRatio = (double) boundingRect.width / boundingRect.height;
            if (aspectRatio >= 0.95 && aspectRatio <= 1.05) {
                shape = "正方形";
            } else {
                shape = "矩形";
            }
        } else if (approx.total() > 4) {
            double area = Imgproc.contourArea(contour);
            // 识别圆形
            Point center = new Point();
            float[] radius = new float[1];
            Imgproc.minEnclosingCircle(contour2f, center, radius);
            double circleArea = Math.PI * radius[0] * radius[0];
            if (Math.abs(area - circleArea) < 0.1 * area) {
                shape = "圆形";
                parameters.add("半径: " + radius[0]);
            } else {
                // 识别椭圆形
                RotatedRect ellipse = Imgproc.fitEllipse(contour2f);
                double majorAxis = Math.max(ellipse.size.width, ellipse.size.height);
                double minorAxis = Math.min(ellipse.size.width, ellipse.size.height);
                if (majorAxis > 0 && minorAxis > 0) {
                    double eccentricity = Math.sqrt(1 - Math.pow(minorAxis / majorAxis, 2));
                    if (eccentricity < 0.2) {
                        shape = "椭圆形";
                        parameters.add("长轴: " + majorAxis);
                        parameters.add("短轴: " + minorAxis);
                    }
                }
            }
        }
        return new ShapeInfo(shape, parameters);
    }

    public static void main(String[] args) {
        try {
            String imagePath = "D:\\tmp\\b1.png";
            int k = 15; // 聚类的数量(提取5种主要颜色)
            List<ColorShapeArea> colorShapeAreas = recognizeColorsAndShapes(imagePath, k);
            // 处理返回的颜色、总轮廓面积和轮廓信息的列表
            for (ColorShapeArea colorShapeArea : colorShapeAreas) {
                Color color = colorShapeArea.getColor();
                double totalArea = colorShapeArea.getTotalArea();
                List<ContourInfo> contourInfos = colorShapeArea.getContourInfos();
                System.out.println("返回的颜色: R=" + color.getRed() + " G=" + color.getGreen() + " B=" + color.getBlue() +
                        " - Total Area: " + totalArea +
                        " - Contour Count: " + contourInfos.size());
                for (ContourInfo contourInfo : contourInfos) {
                    double contourArea = contourInfo.getContourArea();
                    ShapeInfo shapeInfo = contourInfo.getShapeInfo();
                    System.out.println("  返回的轮廓 Area: " + contourArea +
                            " - Shape: " + shapeInfo.getShape() +
                            " - Parameters: " + shapeInfo.getParameters());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/**
 * K-Means聚类算法实现
 */
class KMeansShapeColorX {
    private final int k; // 聚类的数量
    private final List<int[]> data; // 数据点(像素颜色)
    private final List<int[]> centroids; // 聚类中心

    public KMeansShapeColorX(int k, List<int[]> data) {
        this.k = k;
        this.data = data;
        this.centroids = new ArrayList<>();
        initializeCentroids();
    }

    /**
     * 初始化聚类中心
     */
    private void initializeCentroids() {
        for (int i = 0; i < k; i++) {
            int[] randomPixel = data.get((int) (Math.random() * data.size()));
            centroids.add(new int[]{randomPixel[0], randomPixel[1], randomPixel[2]});
        }
    }

    /**
     * 运行K-Means算法
     *
     * @return 返回聚类中心
     */
    public List<int[]> run() {
        boolean changed;
        do {
            List<List<int[]>> clusters = assignPointsToClusters();
            changed = updateCentroids(clusters);
        } while (changed);

        return centroids;
    }

    /**
     * 将数据点分配到最近的聚类中心
     *
     * @return 返回聚类结果
     */
    private List<List<int[]>> assignPointsToClusters() {
        List<List<int[]>> clusters = new ArrayList<>();
        for (int i = 0; i < k; i++) {
            clusters.add(new ArrayList<>());
        }

        for (int[] pixel : data) {
            int closestCentroidIndex = findClosestCentroid(pixel);
            clusters.get(closestCentroidIndex).add(pixel);
        }

        return clusters;
    }

    /**
     * 找到最近的聚类中心
     *
     * @param pixel 像素颜色
     * @return 最近的聚类中心索引
     */
    private int findClosestCentroid(int[] pixel) {
        int closestIndex = 0;
        double minDistance = Double.MAX_VALUE;

        for (int i = 0; i < centroids.size(); i++) {
            double distance = calculateDistance(pixel, centroids.get(i));
            if (distance < minDistance) {
                minDistance = distance;
                closestIndex = i;
            }
        }

        return closestIndex;
    }

    /**
     * 计算两个颜色之间的欧几里得距离
     *
     * @param a 颜色A
     * @param b 颜色B
     * @return 距离
     */
    private double calculateDistance(int[] a, int[] b) {
        return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2) + Math.pow(a[2] - b[2], 2));
    }

    /**
     * 更新聚类中心
     *
     * @param clusters 聚类结果
     * @return 聚类中心是否发生变化
     */
    private boolean updateCentroids(List<List<int[]>> clusters) {
        boolean changed = false;
        for (int i = 0; i < clusters.size(); i++) {
            List<int[]> cluster = clusters.get(i);
            if (cluster.isEmpty()) {
                continue;
            }
            int[] newCentroid = new int[3];
            for (int[] pixel : cluster) {
                newCentroid[0] += pixel[0];
                newCentroid[1] += pixel[1];
                newCentroid[2] += pixel[2];
            }
            newCentroid[0] /= cluster.size();
            newCentroid[1] /= cluster.size();
            newCentroid[2] /= cluster.size();
            if (!equals(newCentroid, centroids.get(i))) {
                centroids.set(i, newCentroid);
                changed = true;
            }
        }
        return changed;
    }

    /**
     * 比较两个颜色是否相同
     *
     * @param a 颜色A
     * @param b 颜色B
     * @return 是否相同
     */
    private boolean equals(int[] a, int[] b) {
        return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
    }
}

/**
 * 存储颜色、总轮廓面积和轮廓信息
 */
class ColorShapeArea {
    private final Color color;
    private final double totalArea;
    private final List<ContourInfo> contourInfos;

    public ColorShapeArea(Color color, double totalArea, List<ContourInfo> contourInfos) {
        this.color = color;
        this.totalArea = totalArea;
        this.contourInfos = contourInfos;
    }

    public Color getColor() {
        return color;
    }

    public double getTotalArea() {
        return totalArea;
    }

    public List<ContourInfo> getContourInfos() {
        return contourInfos;
    }
}

/**
 * 存储轮廓信息(轮廓面积和形状信息)
 */
class ContourInfo {
    private final double contourArea;
    private final ShapeInfo shapeInfo;

    public ContourInfo(double contourArea, ShapeInfo shapeInfo) {
        this.contourArea = contourArea;
        this.shapeInfo = shapeInfo;
    }

    public double getContourArea() {
        return contourArea;
    }

    public ShapeInfo getShapeInfo() {
        return shapeInfo;
    }
}

/**
 * 存储形状信息(形状名称和参数)
 */
class ShapeInfo {
    private final String shape;
    private final List<String> parameters;

    public ShapeInfo(String shape, List<String> parameters) {
        this.shape = shape;
        this.parameters = parameters;
    }

    public String getShape() {
        return shape;
    }

    public List<String> getParameters() {
        return parameters;
    }

    @Override
    public String toString() {
        return String.join(", ", parameters);
    }
}

识别效果:

相关推荐
算法与编程之美16 分钟前
冒泡排序
java·开发语言·数据结构·算法·排序算法
Aphelios38017 分钟前
Java 学习记录:基础到进阶之路(一)
java·开发语言·学习·idea
CV工程师小朱24 分钟前
OpenCV机械臂手眼标定
opencv·机械臂·手眼标定
程序员麻辣烫26 分钟前
晋升系列4:学习方法
java·数据库·程序人生·学习方法
爱学习的小王!33 分钟前
有关MyBatis的动态SQL
java·笔记·sql·学习·mybatis
斑鸠喳喳35 分钟前
模块系统 JPMS
java·后端
苦逼的老王37 分钟前
java之uniapp实现门店地图
java·开发语言·uni-app
austin流川枫39 分钟前
如何基于缓存设计实现一个商品最近搜索记录功能
java·redis
无际单片机编程1 小时前
单片机OTA升级中Bootloader怎么判断APP有没有问题?
java·stm32·单片机·嵌入式硬件·嵌入式