最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中集成OpenCV的操作教程

 更新時(shí)間:2026年07月29日 08:44:07   作者:霸道流氓氣質(zhì)  
本文是一篇結(jié)構(gòu)化的 OpenCV 技術(shù)教程,涵蓋簡(jiǎn)介、核心功能、API 說明、基礎(chǔ)使用示例、完整可運(yùn)行示例工程、業(yè)務(wù)場(chǎng)景實(shí)戰(zhàn)、常見問題與最佳實(shí)踐,以及 OpenCV 與 DJL / SmartJavaAI 的關(guān)系定位,需要的朋友可以參考下

一、OpenCV 簡(jiǎn)介

OpenCV(Open Source Computer Vision Library) 是全球最流行的開源計(jì)算機(jī)視覺與圖像處理庫(kù),最初由 Intel 于 1999 年發(fā)起,主體以 C/C++ 編寫,官方提供 Java、Python、C++ 等多語言綁定。

它解決的核心問題是:把圖像/視頻這一「像素流」轉(zhuǎn)換成可用數(shù)據(jù)——邊框、輪廓、關(guān)鍵點(diǎn)、二值掩碼、縮放/旋轉(zhuǎn)后的圖等「像素級(jí)結(jié)果」。

1.1 關(guān)鍵認(rèn)知

  • OpenCV 只做圖像處理,不訓(xùn)練、也不推理神經(jīng)網(wǎng)絡(luò)。它產(chǎn)出「像素級(jí)結(jié)果」(邊緣圖、二值圖、帶框的圖),而 DJL / SmartJavaAI 產(chǎn)出「語義結(jié)果」(這是什么物體、概率多少)。
  • 在 Java 生態(tài)里,OpenCV 常作為深度學(xué)習(xí)視覺 pipeline 的預(yù)處理與后處理「手術(shù)刀」:切圖、描邊、找輪廓、算像素、畫框。
  • OpenCV 的 Java 綁定通過 JNI 調(diào)用原生 C++ 庫(kù),因此運(yùn)行前必須先加載平臺(tái)對(duì)應(yīng)的 opencv_java*.dll / .so / .dylib。

1.2 資源

  • 官方倉(cāng)庫(kù):https://github.com/opencv/opencv
  • 官方文檔:https://docs.opencv.org/4.x/
  • 人臉/物體檢測(cè)權(quán)重:opencv/data/haarcascades/

二、核心功能概覽

功能域典型能力 主要 Java 類
圖像 / 視頻 IO讀圖、寫圖、攝像頭捕獲、視頻讀寫Imgcodecs、VideoCaptureVideoWriter
基礎(chǔ)圖像處理灰度化、色彩空間轉(zhuǎn)換、模糊、閾值、 morphologicalImgproc、Core
幾何變換縮放、旋轉(zhuǎn)、仿射/透 視變換、裁剪Imgproc、Core
邊緣與輪廓Canny 邊緣檢測(cè)、輪廓提取、輪廓屬性分析Imgproc
繪制畫矩形/圓/線/文字、填充Imgproc
特征與輪廓描述輪廓外接矩形/圓、面積、周長(zhǎng)Imgproc、Core
經(jīng)典機(jī)器學(xué)習(xí)視覺Haar 級(jí)聯(lián)人臉檢測(cè)、HOG、特征點(diǎn)objdetect.CascadeClassifier
高級(jí)模塊相機(jī)標(biāo)定 calib3d、目標(biāo)跟蹤 video、背景減除Calib3dVideo、Photo

三、核心概念與數(shù)據(jù)結(jié)構(gòu)

概念說明
native 庫(kù)(核心差異)OpenCV 主體是 C++,Java 通過 JNI 調(diào)用。必須先加載平臺(tái)對(duì)應(yīng)的 .dll/.so/.tylib。工程用 nu.pattern.OpenCV.loadLocally() 自動(dòng)下載并加載。
MatOpenCV 的核心數(shù)據(jù)結(jié)構(gòu)「多維稠密數(shù)組」,用于表示圖像(H×W×C 像素矩陣)。release() 釋放底層內(nèi)存。Mat 不實(shí)現(xiàn) AutoCloseable,必須手動(dòng) release()
Imgcodecs圖像文件讀寫:imread(path) / imwrite(path, mat)。
Imgproc圖像處理算子大本營(yíng):cvtColorGaussianBlur、Canny、thresholdfindContours、rectanglecircle、lineputText、drawContours。
Core基礎(chǔ)運(yùn)算與常量:Core.VERSIONCore.countNonZero 等。
Scalar四通道顏色值 (B, G, R, A)。OpenCV 默認(rèn) BGR 順序,不是 RGB。
Size / Point / Rect幾何基礎(chǔ)類型:尺寸、點(diǎn)、矩形。
CascadeClassifierHaar/級(jí)聯(lián)分類器,用于人臉等目標(biāo)檢測(cè);構(gòu)造需傳入訓(xùn)練好的 .xml 權(quán)重。
CvType像素類型常量,如 CvType.CV_8UC3 表示 8 位無符號(hào)、3 通道(彩色)。

四、Java 環(huán)境準(zhǔn)備

4.1 依賴(Maven)

org.openpnp:opencv 是官方 Java 綁定的 Maven 封裝,會(huì)自動(dòng)下載對(duì)應(yīng)平臺(tái)的 native 庫(kù),API 與 OpenCV 官方文檔完全一致

<properties>
    <java.version>17</java.version>
    <opencv.version>4.7.0-0</opencv.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.openpnp</groupId>
        <artifactId>opencv</artifactId>
        <version>${opencv.version}</version>
    </dependency>
</dependencies>

本工程是 Spring Boot 工程,還引入了 spring-boot-starter-web、spring-boot-starter-test、lombok。

4.2 native 庫(kù)加載

首次調(diào)用 OpenCV.loadLocally() 時(shí):

  1. 探測(cè) OS + 架構(gòu)(Windows-x86_64 / linux-x86_64 / osx-aarch_64 …)
  2. 從 GitHub(github.com/openpnp/opencv 發(fā)布的 native 包)下載對(duì)應(yīng) zip
  3. 解壓到用戶目錄(如 C:\Users\<user>\.cache\opencv\)并 System.load
  4. 之后復(fù)用,無需重復(fù)下載
import nu.pattern.OpenCV;
import org.opencv.core.Core;

OpenCV.loadLocally();
System.out.println("OpenCV version = " + Core.VERSION);

4.3 人臉檢測(cè)權(quán)重(可選)

Haar 級(jí)聯(lián)分類器需要訓(xùn)練好的 .xml

models/haarcascade_frontalface_default.xml

獲?。?code>https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml,放到工程根目錄 models/ 下即可。

五、核心 API 說明

約定:src 源 Mat,dst 目標(biāo) Mat,gray 灰度 Mat。OpenCV 算子幾乎都是 Imgproc靜態(tài)方法。

5.1 圖像 IO ——Imgcodecs

Mat src = Imgcodecs.imread("D:/in.jpg");   // 讀圖,失敗返回 empty Mat
if (src.empty()) { /* 處理讀取失敗 */ }
boolean ok = Imgcodecs.imwrite("D:/out.png", src);  // 寫圖,返回是否成功

5.2 顏色空間 ——Imgproc.cvtColor

Mat gray = new Mat();
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);   // BGR -> 灰度
Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGR2HSV);    // BGR -> HSV(常用于顏色分割)

OpenCV 默認(rèn) BGR 通道順序,與常見 RGB 相反。

5.3 模糊 / 降噪 ——Imgproc.GaussianBlur

Imgproc.GaussianBlur(src, dst, new Size(5, 5), 0);

核大?。ǖ谌齻€(gè)參數(shù) Size)通常取奇數(shù),越大越模糊。

5.4 邊緣檢測(cè) ——Imgproc.Canny

Mat edges = new Mat();
Imgproc.Canny(gray, edges, 100, 200);   // 雙閾值(低,高),結(jié)果二值圖
int edgePoints = Core.countNonZero(edges);  // 統(tǒng)計(jì)邊緣像素?cái)?shù)

5.5 閾值 ——Imgproc.threshold

// 固定閾值
Imgproc.threshold(gray, binary, 127, 255, Imgproc.THRESH_BINARY);
// OTSU 自動(dòng)閾值(返回實(shí)際閾值)
double t = Imgproc.threshold(gray, binary, 0, 255,
        Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);

5.6 輪廓提取 ——Imgproc.findContours+drawContours

List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(binary, contours, hierarchy,
        Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);  // 入?yún)⒈仨毷嵌祱D
Imgproc.drawContours(src, contours, -1, new Scalar(0, 0, 255), 2);  // -1 畫全部輪廓

常用輪廓屬性:

for (MatOfPoint c : contours) {
    Rect r = Imgproc.boundingRect(c);     // 外接矩形
    double area = Imgproc.contourArea(c); // 面積
    // 外接圓
    Point center = new Point();
    float[] radius = new float[1];
    Imgproc.minEnclosingCircle(new MatOfPoint2f(c.toArray()), center, radius);
}

5.7 繪制 ——Imgproc.rectangle / circle / line / putText

Scalar red = new Scalar(0, 0, 255);     // BGR:紅
Imgproc.rectangle(canvas, new Point(30, 30), new Point(170, 130), red, 3);  // 線寬3
Imgproc.rectangle(canvas, new Point(30,30), new Point(170,130), red, -1);   // 線寬-1=實(shí)心填充
Imgproc.circle(canvas, new Point(300, 200), 60, new Scalar(0, 255, 0), -1); // 綠圓實(shí)心
Imgproc.line(canvas, new Point(20, 260), new Point(380, 260), new Scalar(0,0,255), 2);
Imgproc.putText(canvas, "Hello OpenCV", new Point(60, 290),
        Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar(0,0,0), 2);

5.8 人臉檢測(cè) ——objdetect.CascadeClassifier

CascadeClassifier cascade = new CascadeClassifier("models/haarcascade_frontalface_default.xml");
MatOfRect faces = new MatOfRect();
cascade.detectMultiScale(gray, faces);   // 輸入灰度圖
for (Rect r : faces.toArray()) {
    Imgproc.rectangle(src, r, new Scalar(0, 255, 0), 2);  // 給每張臉畫綠框
}

5.9 創(chuàng)建畫布 —— 構(gòu)造Mat

Mat canvas = new Mat(300, 400, CvType.CV_8UC3, new Scalar(255, 255, 255)); // 白底 400x300

new Mat(height, width, type, scalar):注意構(gòu)造順序是 (高, 寬),與日常 (寬, 高) 相反。

5.10 內(nèi)存釋放(重要)

Mat 不實(shí)現(xiàn) AutoCloseable,不能用 try-with-resources。必須 try/finally 手動(dòng)釋放,否則會(huì)內(nèi)存/顯存泄漏:

Mat src = Imgcodecs.imread(in);
try {
    Mat dst = new Mat();
    Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGR2GRAY);
    Imgcodecs.imwrite(out, dst);
    dst.release();
} finally {
    src.release();
}

六、基礎(chǔ)使用示例

6.1 灰度化(最小可用)

Mat src = Imgcodecs.imread("D:/in.jpg");
Mat gray = new Mat();
try {
    Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
    Imgcodecs.imwrite("D:/gray.jpg", gray);
} finally {
    src.release(); gray.release();
}

6.2 Canny 邊緣檢測(cè)

Mat src = Imgcodecs.imread("D:/in.jpg");
Mat gray = new Mat();
Mat edges = new Mat();
try {
    Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
    Imgproc.GaussianBlur(gray, gray, new Size(3, 3), 0); // 先降噪效果好
    Imgproc.Canny(gray, edges, 100, 200);
    Imgcodecs.imwrite("D:/edges.png", edges);
    System.out.println("邊緣點(diǎn)數(shù)=" + Core.countNonZero(edges));
} finally {
    src.release(); gray.release(); edges.release();
}

6.3 不依賴任何圖片的「繪制」

Mat 可以直接當(dāng)畫布,因此可以離線、無輸入圖必跑,是驗(yàn)證環(huán)境是否就緒的最佳用例:

Mat canvas = new Mat(300, 400, CvType.CV_8UC3, new Scalar(255, 255, 255));
Imgproc.rectangle(canvas, new Point(30,30), new Point(170,130), new Scalar(255,0,0), 3);
Imgproc.putText(canvas, "OpenCV", new Point(60,290),
        Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar(0,0,0), 2);
Imgcodecs.imwrite("D:/draw.png", canvas);
canvas.release();

七、完整可運(yùn)行示例工程

下面給出示例 opencvDemo 工程的核心代碼,工程目錄:

opencvDemo/
├── pom.xml
├── models/                          # 可選:haarcascade_frontalface_default.xml
├── src/main/java/com/badao/demo/
│   ├── DemoApplication.java
│   ├── config/OpenCvConfig.java     # 啟動(dòng)時(shí)預(yù)加載 native
│   ├── service/OpenCvService.java   # 圖像處理能力
│   └── controller/OpenCvController.java
└── src/test/java/com/badao/demo/OpenCvAllFeatureTest.java

7.1 啟動(dòng)類

package com.badao.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

7.2 native 預(yù)加載配置

package com.badao.demo.config;

import lombok.extern.slf4j.Slf4j;
import nu.pattern.OpenCV;
import org.opencv.core.Core;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class OpenCvConfig {
    @Bean
    public Object openCvInitializer() {
        try {
            OpenCV.loadLocally();
            log.info("OpenCV native 已加載,版本 = {}", Core.VERSION);
        } catch (Exception | UnsatisfiedLinkError e) {
            log.warn("OpenCV native 加載失?。x線環(huán)境將無法運(yùn)行圖像處理): {}", e.getMessage());
        }
        return new Object();
    }
}

7.3 核心 Service(灰度 / 邊緣 / 輪廓 / 繪制 / 人臉)

package com.badao.demo.service;

import nu.pattern.OpenCV;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.springframework.stereotype.Service;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

@Service
public class OpenCvService {

    private static final String FACE_CASCADE = "models/haarcascade_frontalface_default.xml";

    private void ensureLoaded() {
        try {
            OpenCV.loadLocally();
        } catch (Exception | UnsatisfiedLinkError e) {
            throw new IllegalStateException("OpenCV native 加載失敗,需聯(lián)網(wǎng)下載原生庫(kù): " + e.getMessage(), e);
        }
    }

    public String version() {
        ensureLoaded();
        return Core.VERSION;
    }

    /** 灰度化 */
    public String grayscale(String inPath, String outPath) {
        Mat src = read(inPath);
        Mat dst = new Mat();
        try {
            Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGR2GRAY);
            boolean ok = Imgcodecs.imwrite(outPath, dst);
            return String.format("灰度化完成 -> %s (寫入%s, 尺寸=%dx%d)",
                    outPath, ok ? "成功" : "失敗", dst.cols(), dst.rows());
        } finally {
            src.release(); dst.release();
        }
    }

    /** Canny 邊緣檢測(cè) */
    public String canny(String inPath, String outPath) {
        Mat src = read(inPath);
        Mat gray = new Mat();
        Mat edges = new Mat();
        try {
            Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
            Imgproc.GaussianBlur(gray, gray, new Size(3, 3), 0);
            Imgproc.Canny(gray, edges, 100, 200);
            boolean ok = Imgcodecs.imwrite(outPath, edges);
            return String.format("Canny 邊緣檢測(cè)完成 -> %s (寫入%s, 邊緣點(diǎn)數(shù)=%d)",
                    outPath, ok ? "成功" : "失敗", Core.countNonZero(edges));
        } finally {
            src.release(); gray.release(); edges.release();
        }
    }

    /** 輪廓提取 + 繪制 */
    public String contours(String inPath, String outPath) {
        Mat src = read(inPath);
        Mat gray = new Mat();
        Mat binary = new Mat();
        Mat hierarchy = new Mat();
        try {
            Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
            Imgproc.threshold(gray, binary, 127, 255, Imgproc.THRESH_BINARY);
            List<MatOfPoint> contours = new ArrayList<>();
            Imgproc.findContours(binary, contours, hierarchy,
                    Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
            Imgproc.drawContours(src, contours, -1, new Scalar(0, 0, 255), 2);
            boolean ok = Imgcodecs.imwrite(outPath, src);
            return String.format("輪廓提取完成(共%d個(gè)輪廓) -> %s (寫入%s)",
                    contours.size(), outPath, ok ? "成功" : "失敗");
        } finally {
            src.release(); gray.release(); binary.release(); hierarchy.release();
        }
    }

    /** 圖形繪制(不依賴輸入圖片,必定可跑) */
    public String drawShapes(String outPath) {
        ensureLoaded();
        Mat canvas = new Mat(300, 400, CvType.CV_8UC3, new Scalar(255, 255, 255));
        try {
            Imgproc.rectangle(canvas, new Point(30, 30), new Point(170, 130), new Scalar(255, 0, 0), 3);
            Imgproc.circle(canvas, new Point(300, 200), 60, new Scalar(0, 255, 0), -1);
            Imgproc.line(canvas, new Point(20, 260), new Point(380, 260), new Scalar(0, 0, 255), 2);
            Imgproc.putText(canvas, "Hello OpenCV", new Point(60, 290),
                    Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar(0, 0, 0), 2);
            boolean ok = Imgcodecs.imwrite(outPath, canvas);
            return String.format("繪制圖形完成 -> %s (寫入%s, 畫布=400x300)",
                    outPath, ok ? "成功" : "失敗");
        } finally {
            canvas.release();
        }
    }

    /** 人臉檢測(cè)(需 haarcascade 權(quán)重) */
    public String faceDetect(String inPath, String outPath) {
        Path xml = Path.of(FACE_CASCADE);
        if (!Files.exists(xml)) {
            throw new IllegalStateException(
                    "缺少人臉檢測(cè)權(quán)重 " + FACE_CASCADE + ",可從 OpenCV 倉(cāng)庫(kù)下載放入 ./models/");
        }
        Mat src = read(inPath);
        Mat gray = new Mat();
        CascadeClassifier cascade = new CascadeClassifier(xml.toString());
        try {
            Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
            MatOfRect faces = new MatOfRect();
            cascade.detectMultiScale(gray, faces);
            for (Rect r : faces.toArray()) {
                Imgproc.rectangle(src, r, new Scalar(0, 255, 0), 2);
            }
            boolean ok = Imgcodecs.imwrite(outPath, src);
            return String.format("人臉檢測(cè)完成(命中%d張臉) -> %s (寫入%s)",
                    faces.toArray().length, outPath, ok ? "成功" : "失敗");
        } finally {
            src.release(); gray.release();
        }
    }

    private Mat read(String inPath) {
        ensureLoaded();
        Mat mat = Imgcodecs.imread(inPath);
        if (mat.empty()) {
            throw new IllegalArgumentException("無法讀取圖片: " + inPath);
        }
        return mat;
    }
}

7.4 HTTP 演示接口

package com.badao.demo.controller;

import com.badao.demo.service.OpenCvService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/opencv")
public class OpenCvController {

    @Autowired
    private OpenCvService openCvService;

    @GetMapping("/version")
    public Map<String, Object> version() {
        return wrap(() -> openCvService.version());
    }

    @GetMapping("/grayscale")
    public Map<String, Object> grayscale(@RequestParam String in, @RequestParam String out) {
        return wrap(() -> openCvService.grayscale(in, out));
    }

    @GetMapping("/canny")
    public Map<String, Object> canny(@RequestParam String in, @RequestParam String out) {
        return wrap(() -> openCvService.canny(in, out));
    }

    @GetMapping("/contours")
    public Map<String, Object> contours(@RequestParam String in, @RequestParam String out) {
        return wrap(() -> openCvService.contours(in, out));
    }

    @GetMapping("/draw")
    public Map<String, Object> draw(@RequestParam String out) {
        return wrap(() -> openCvService.drawShapes(out));
    }

    @GetMapping("/face")
    public Map<String, Object> face(@RequestParam String in, @RequestParam String out) {
        return wrap(() -> openCvService.faceDetect(in, out));
    }

    private Map<String, Object> wrap(Op op) {
        Map<String, Object> resp = new HashMap<>();
        try {
            resp.put("success", true);
            resp.put("message", op.run());
        } catch (Exception e) {
            resp.put("success", false);
            resp.put("message", e.getMessage());
        }
        return resp;
    }

    @FunctionalInterface
    private interface Op { String run(); }
}

7.5 運(yùn)行與調(diào)用

cd opencvDemo
mvn compile
mvn spring-boot:run          # 端口 998

# 調(diào)用(首次會(huì)聯(lián)網(wǎng)下載 native,請(qǐng)耐心等待)
curl "http://localhost:998/opencv/version"
curl "http://localhost:998/opencv/draw?out=./out/draw.png"
curl "http://localhost:998/opencv/grayscale?in=D:/t.jpg&out=./out/gray.png"
curl "http://localhost:998/opencv/canny?in=D:/t.jpg&out=./out/canny.png"
curl "http://localhost:998/opencv/contours?in=D:/shapes.png&out=./out/contours.png"
curl "http://localhost:998/opencv/face?in=D:/faces.jpg&out=./out/faces.png"

7.6 單元測(cè)試(優(yōu)雅降級(jí))

測(cè)試采用「能跑就跑、缺依賴就跳過」機(jī)制:version() / drawShapes() 只依賴 native;其余用例在離線或缺權(quán)重時(shí)通過 Assumptions.assumeTrue(false, ...) 自動(dòng)跳過,保證離線也能 BUILD SUCCESS。

mvn test

八、業(yè)務(wù)場(chǎng)景實(shí)戰(zhàn)

下面給出 6 個(gè)真實(shí)業(yè)務(wù)中常見的 OpenCV 落地場(chǎng)景,全部基于上文 API,可直接復(fù)用。

8.1 場(chǎng)景一:證件照/頭像自動(dòng)灰度與裁邊

業(yè)務(wù):用戶上傳證件照,系統(tǒng)統(tǒng)一轉(zhuǎn)灰度、自動(dòng)裁剪白邊、縮放到標(biāo)準(zhǔn)尺寸(如 295×413 一寸照)。

public Mat normalizeIdPhoto(String inPath) {
    Mat src = Imgcodecs.imread(inPath);
    Mat gray = new Mat();
    Mat binary = new Mat();
    try {
        Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
        // 自動(dòng)閾值 -> 找前景輪廓 -> 取最大外接矩形裁邊
        Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY_INV | Imgproc.THRESH_OTSU);
        List<MatOfPoint> contours = new ArrayList<>();
        Mat hier = new Mat();
        Imgproc.findContours(binary, contours, hier, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
        Rect best = null;
        for (MatOfPoint c : contours) {
            Rect r = Imgproc.boundingRect(c);
            if (best == null || r.area() > best.area()) best = r;
        }
        Mat crop = new Mat(src, best != null ? best : new Rect(0, 0, src.cols(), src.rows()));
        Mat resized = new Mat();
        Imgproc.resize(crop, resized, new Size(295, 413)); // 標(biāo)準(zhǔn)一寸照
        return resized;
    } finally {
        src.release(); gray.release(); binary.release();
    }
}

8.2 場(chǎng)景二:工業(yè)零件缺陷檢測(cè)(邊緣 + 輪廓面積)

業(yè)務(wù):產(chǎn)線拍照檢測(cè)零件是否有缺角/毛刺——通過 Canny + 輪廓面積偏離正常區(qū)間來判定。

public boolean isDefect(String partPath, double stdArea, double tol) {
    Mat src = Imgcodecs.imread(partPath);
    Mat gray = new Mat(), edges = new Mat(), binary = new Mat(), hier = new Mat();
    try {
        Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
        Imgproc.GaussianBlur(gray, gray, new Size(5, 5), 0);
        Imgproc.Canny(gray, edges, 50, 150);
        Imgproc.threshold(edges, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
        List<MatOfPoint> contours = new ArrayList<>();
        Imgproc.findContours(binary, contours, hier, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
        double area = contours.stream().mapToDouble(Imgproc::contourArea).sum();
        return Math.abs(area - stdArea) > stdArea * tol; // 偏離閾值即判定缺陷
    } finally {
        src.release(); gray.release(); edges.release(); binary.release(); hier.release();
    }
}

8.3 場(chǎng)景三:人臉打卡 / 門禁(Haar 人臉檢測(cè))

業(yè)務(wù):攝像頭抓拍后,用 Haar 級(jí)聯(lián)定位人臉區(qū)域,裁剪出人臉供后續(xù)比對(duì)或存檔。

public List<Mat> detectFaces(String photoPath) {
    CascadeClassifier cascade = new CascadeClassifier("models/haarcascade_frontalface_default.xml");
    Mat src = Imgcodecs.imread(photoPath);
    Mat gray = new Mat();
    List<Mat> faces = new ArrayList<>();
    try {
        Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
        MatOfRect rects = new MatOfRect();
        cascade.detectMultiScale(gray, rects);
        for (Rect r : rects.toArray()) {
            faces.add(new Mat(src, r)); // 裁剪出每張臉
        }
        return faces;
    } finally {
        src.release(); gray.release();
    }
}

8.4 場(chǎng)景四:文檔掃描 / 透 視矯正

業(yè)務(wù):手機(jī)拍的文檔歪斜,先找最大輪廓四邊形,再做透 視變換拉正。

// 關(guān)鍵步驟:找最大輪廓 -> 求外接四邊形四點(diǎn) -> warpPerspective 矯正
// 此處給出骨架,findContours + 多邊形逼近 + getPerspectiveTransform 即可實(shí)現(xiàn)。
Mat doc = Imgcodecs.imread("D:/doc.jpg");
Mat gray = new Mat(), binary = new Mat();
Imgproc.cvtColor(doc, gray, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
List<MatOfPoint> contours = new ArrayList<>(); Mat hier = new Mat();
Imgproc.findContours(binary, contours, hier, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// 取面積最大的輪廓,近似多邊形(Imgproc.approxPolyDP),若 4 個(gè)頂點(diǎn)則可用
// Core.getPerspectiveTransform(srcPts, dstPts) + Imgproc.warpPerspective(...) 完成矯正。
doc.release(); gray.release(); binary.release(); hier.release();

8.5 場(chǎng)景五:視頻抽幀 + 移動(dòng)偵測(cè)(背景減除)

業(yè)務(wù):監(jiān)控視頻中只對(duì)「有變化」的幀做處理/告警,降成本。

// 借助 video 模塊的背景減除器
// BackgroundSubtractorMOG2 mog = Video.createBackgroundSubtractorMOG2();
// mog.apply(frame, fgMask);  -> fgMask 即前景(運(yùn)動(dòng)區(qū)域)二值圖
// 再對(duì) fgMask 做 findContours + 面積過濾即可定位移動(dòng)目標(biāo)。

8.6 場(chǎng)景六:AI 推理的前/后處理(OpenCV + DJL 協(xié)作)

業(yè)務(wù):目標(biāo)檢測(cè)模型輸出一堆坐標(biāo),OpenCV 負(fù)責(zé)畫框、裁剪、NMS 后處理。

// 假設(shè)模型返回 detections: List<Detection{x,y,w,h,label}>
Mat img = Imgcodecs.imread("D:/scene.jpg");
for (Detection d : detections) {
    Rect r = new Rect(d.x, d.y, d.w, d.h);
    Imgproc.rectangle(img, r, new Scalar(0, 255, 0), 2);
    Imgproc.putText(img, d.label, new Point(d.x, d.y - 5),
            Imgproc.FONT_HERSHEY_SIMPLEX, 0.6, new Scalar(0, 255, 0), 2);
}
Imgcodecs.imwrite("D:/detected.jpg", img);
img.release();

九、常見問題與最佳實(shí)踐

現(xiàn)象原因 / 解決
UnsatisfiedLinkError / native 加載失敗首次運(yùn)行需聯(lián)網(wǎng)下載 native;離線環(huán)境會(huì)跳過相關(guān)測(cè)試/接口返回失敗。
try-with-resources 編譯報(bào)錯(cuò) Mat 無法轉(zhuǎn)換為 AutoCloseableOpenCV 的 Mat/CascadeClassifier 不實(shí)現(xiàn) AutoCloseable,必須用 try/finally + mat.release()
顏色怪異(紅藍(lán)顛倒)OpenCV 默認(rèn) BGR,Scalar 要寫成 (B, G, R)。
人臉檢測(cè)返回「缺少權(quán)重」haarcascade_frontalface_default.xml 放到 ./models/
圖像讀取為空 / 中文路徑失敗imread 對(duì)部分中文/特殊路徑不友好,可先用 Files 復(fù)制到 ASCII 路徑,或用 imdecode + BytePointer 讀字節(jié)流。
內(nèi)存持續(xù)增長(zhǎng)每個(gè) Mat 用完必須 release(),尤其循環(huán)處理視頻幀時(shí)。
接口返回 success=false多為離線無法下載 native;聯(lián)網(wǎng)后重試。

最佳實(shí)踐

  1. 統(tǒng)一封裝 try/finally 釋放:建議寫一個(gè) Mat 工具類或 use(mat, m -> {...}) 函數(shù)式封裝,杜絕泄漏。
  2. 先灰度后處理:絕大多數(shù)算子(Canny、閾值、級(jí)聯(lián)檢測(cè))輸入灰度的效果與性能都更好。
  3. 先降噪再邊緣:Canny 前加 GaussianBlur 可顯著減少噪點(diǎn)邊緣。
  4. 優(yōu)雅降級(jí):生產(chǎn)環(huán)境加載 native 失敗時(shí)不應(yīng)讓應(yīng)用崩潰,應(yīng)告警并降級(jí)(參考本工程 OpenCvConfig)。

十、OpenCV 與 DJL / SmartJavaAI 的關(guān)系

            ┌─────────────────────┐
            │  你的業(yè)務(wù)系統(tǒng)(Spring) │
            └──────────┬──────────┘
                       │
        ┌──────────────┼───────────────────┐
        │              │                   │
  OpenCV(圖像處理)   DJL(張量/推理引擎)   SmartJavaAI(封裝好的 AI 能力)
   像素級(jí)原語         NDArray + 模型推理     人臉識(shí)別/ OCR/ 檢測(cè)…
        │              │                   │
        └──────┬───────┴───────────────────┘
               │  SmartJavaAI 底層大量調(diào)用 OpenCV 做預(yù)處理/畫框
               ▼
          OpenCV 是它們的「畫筆與手術(shù)刀」
  • OpenCV:給你「手術(shù)刀」——切圖、描邊、找輪廓、算像素。自己不產(chǎn)生語義結(jié)論。
  • DJL:給你「大腦」——加載 ResNet/SSD/onnx 模型做張量推理,輸出分類/檢測(cè)框。
  • SmartJavaAI:把上面兩者包成「即開即用能力」——你只傳圖,它返回「這是貓 / 框出人臉」。
  • 三者互補(bǔ)不替代:寫一個(gè) AI 應(yīng)用通常 = OpenCV 做預(yù)處理與后處理 + DJL/SmartJavaAI 做推理。

附錄:API 速查表

能力核心 API關(guān)鍵參數(shù)/常量備注
加載 nativeOpenCV.loadLocally()首次聯(lián)網(wǎng)下載,冪等
版本Core.VERSION字符串如 "4.7.0"
讀圖Imgcodecs.imread(path)返回 Mat;空?qǐng)D用 mat.empty() 判錯(cuò)
寫圖Imgcodecs.imwrite(path, mat)返回 boolean
顏色空間Imgproc.cvtColor(src, dst, code)COLOR_BGR2GRAYOpenCV 默認(rèn) BGR
高斯模糊Imgproc.GaussianBlur(src, dst, new Size(k,k), 0)核大小需奇數(shù)
邊緣檢測(cè)Imgproc.Canny(gray, edges, low, high)雙閾值結(jié)果二值圖
閾值Imgproc.threshold(gray, dst, 0, 255, THRESH_BINARY|THRESH_OTSU)OTSU 自動(dòng)閾值返回實(shí)際閾值
輪廓Imgproc.findContours(bin, list, hier, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE)入?yún)⒍祱D
畫輪廓Imgproc.drawContours(img, list, -1, new Scalar(b,g,r), thick)idx=-1 畫全部
畫矩形Imgproc.rectangle(img, p1, p2, scalar, thick)thick=-1 實(shí)心
畫圓Imgproc.circle(img, center, r, scalar, thick)
畫線Imgproc.line(img, p1, p2, scalar, thick)
寫字Imgproc.putText(img, text, p, FONT_HERSHEY_SIMPLEX, scale, scalar, thick)
外接矩形Imgproc.boundingRect(contour)返回 Rect
輪廓面積Imgproc.contourArea(contour)double
人臉檢測(cè)new CascadeClassifier(xml); cascade.detectMultiScale(gray, MatOfRect).xml 權(quán)重
顏色new Scalar(B, G, R, A)注意 BGR 順序
畫布new Mat(h, w, CvType.CV_8UC3, new Scalar(...))CV_8UC3=彩色構(gòu)造順序(高,寬)
像素統(tǒng)計(jì)Core.countNonZero(mat)用于數(shù)邊緣/前景點(diǎn)
釋放mat.release()必須手動(dòng)Mat 非 AutoCloseable

通用范式

Mat src = Imgcodecs.imread(in);
try {
    Mat dst = new Mat();
    Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGR2GRAY);
    Imgcodecs.imwrite(out, dst);
    dst.release();
} finally {
    src.release();   // OpenCV 的 Mat 不會(huì)自動(dòng)關(guān)閉,務(wù)必 release
}

以上就是Java中集成OpenCV的操作教程的詳細(xì)內(nèi)容,更多關(guān)于Java集成OpenCV的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • springboot注解之@Conditional使用解析

    springboot注解之@Conditional使用解析

    這篇文章主要介紹了springboot注解之@Conditional使用解析,conditional 這個(gè)英文單詞翻譯過來是有條件的,所以 @Conditional 注解是作為條件存在的,如果滿足配置的條件則執(zhí)行,如果沒有滿足的話就不執(zhí)行,需要的朋友可以參考下
    2023-11-11
  • spring boot實(shí)現(xiàn)圖片上傳和下載功能

    spring boot實(shí)現(xiàn)圖片上傳和下載功能

    這篇文章主要為大家詳細(xì)介紹了spring boot實(shí)現(xiàn)圖片上傳和下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • javaweb圖書商城設(shè)計(jì)之分類模塊(2)

    javaweb圖書商城設(shè)計(jì)之分類模塊(2)

    這篇文章主要為大家詳細(xì)介紹了javaweb圖書商城設(shè)計(jì)之分類模塊的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Springboot Activemq整合過程代碼圖解

    Springboot Activemq整合過程代碼圖解

    這篇文章主要介紹了Springboot Activemq整合過程代碼圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • MyBatis處理懶加載和預(yù)加載的實(shí)戰(zhàn)詳解

    MyBatis處理懶加載和預(yù)加載的實(shí)戰(zhàn)詳解

    在Java開發(fā)中,MyBatis是一個(gè)廣泛使用的持久層框架,它為開發(fā)者提供了靈活、強(qiáng)大的數(shù)據(jù)訪問能力,兩種常用的加載策略是懶加載和預(yù)加載,本文將介紹這兩種加載方式的概念、適用場(chǎng)景以及MyBatis中的實(shí)現(xiàn)方式,需要的朋友可以參考下
    2026-02-02
  • idea導(dǎo)入jar包的詳細(xì)圖文教程

    idea導(dǎo)入jar包的詳細(xì)圖文教程

    這篇文章主要給大家介紹了關(guān)于idea導(dǎo)入jar包的詳細(xì)圖文教程,文中通過圖文將導(dǎo)入的步驟介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-03-03
  • SpringBoot接口冪等性4種解決方案+避坑實(shí)戰(zhàn)指南

    SpringBoot接口冪等性4種解決方案+避坑實(shí)戰(zhàn)指南

    接口冪等性,看似簡(jiǎn)單,實(shí)則關(guān)乎資金安全、用戶體驗(yàn)和系統(tǒng)穩(wěn)定性,今天,咱們就來聊聊在SpringBoot項(xiàng)目中,如何真正落地接口冪等性,避免成為“背鍋俠”,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • Java線程池隊(duì)列DelayQueue的使用詳解

    Java線程池隊(duì)列DelayQueue的使用詳解

    這篇文章主要介紹了Java線程池隊(duì)列DelayQueue的使用詳解,DelayQueue顧名思義,它是個(gè)無邊界延遲隊(duì)列,它的底層是基于PriorityBlockingQueue實(shí)現(xiàn)的,該隊(duì)列中的元素都是按照過期時(shí)間順序排序的,隊(duì)列頭部放的是即將過期的元素,需要的朋友可以參考下
    2023-12-12
  • grpc-java?k8s下的負(fù)載均衡處理方法

    grpc-java?k8s下的負(fù)載均衡處理方法

    這篇文章主要為大家介紹了grpc-java?k8s下的負(fù)載均衡的處理方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-02-02
  • spring boot整合shiro安全框架過程解析

    spring boot整合shiro安全框架過程解析

    這篇文章主要介紹了spring boot整合shiro安全框架過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11

最新評(píng)論

涞水县| 临沭县| 仙桃市| 上饶市| 皮山县| 凌海市| 昌乐县| 象山县| 容城县| 玛沁县| 瓮安县| 开封市| 澄城县| 民乐县| 馆陶县| 轮台县| 龙胜| 灵璧县| 鲁甸县| 乐亭县| 军事| 三都| 南昌市| 玉田县| 海南省| 特克斯县| 陈巴尔虎旗| 西安市| 婺源县| 安顺市| 沂源县| 望谟县| 沙雅县| 双辽市| 剑河县| 屯留县| 永嘉县| 新野县| 中超| 博野县| 包头市|