opencv輪廓findContours&drawContours


本文目的

目的:學習使用opencv的findContours和drawContours函數

語言:java

版本:opencv-410

簡介:通過findContours函數檢測物體輪廓,並且用drawContours畫出來

程序支持效果:

加載圖片後可以在界面上更改三個參數進行效果對比查看

· 1.修改邊緣檢測閾值,改變邊緣檢測效果

· 2.修改輪廓檢索模式

· 3.修改輪廓的近似模式


分解介紹

函數:findContours


<code>findContours(Mat image,

List<matofpoint> contours,

Mat hierarchy,

int mode,

int method,

Point offset)/<matofpoint>/<code>


參數介紹

· 第一個參數:image:單通道圖像矩陣,可以是灰度圖,但更常用的是二值圖像,一般是經過Canny、拉普拉斯等邊緣檢測算子處理過的二值圖像;

· 第二個參數:contours,定義為"vector<vector>> contours",是一個向量,並且是一個雙重向量,向量內每個元素保存了一組由連續的Point點構成的點的集合的向量,每一組Point點集就是一個輪廓。有多少輪廓,向量contours就有多少元素。/<vector>

· 第三個參數:存儲了檢出的輪廓層級結構,具體學習參考別人的文檔:

· 第四個參數:int型的mode,定義輪廓的檢索模式:

· RETR_EXTERNAL:只檢測最外圍輪廓,包含在外圍輪廓內的內圍輪廓被忽略

·

RETR_LIST :檢測所有的輪廓,包括內圍、外圍輪廓,但是檢測到的輪廓不建立等級關係,彼此之間獨立,沒有等級關係,這就意味著這個檢索模式下不存在父輪廓或內嵌輪廓

· RETR_CCOMP: 檢測所有的輪廓,但所有輪廓只建立兩個等級關係,外圍為頂層,若外圍內的內圍輪廓還包含了其他的輪廓信息,則內圍內的所有輪廓均歸屬於頂層

· RETR_TREE:檢測所有輪廓,所有輪廓建立一個等級樹結構。外層輪廓包含內層輪廓,內層輪廓還可以繼續包含內嵌輪廓。

· 第五個參數:int型的method,定義輪廓的近似方法:

· CHAIN_APPROX_NONE:保存物體邊界上所有連續的輪廓點到contours向量內

· CHAIN_APPROX_SIMPLE:僅保存輪廓的拐點信息,把所有輪廓拐點處的點保存入contours向量內,拐點與拐點之間直線段上的信息點不予保留

· CHAIN_APPROX_TC89_L1

CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

· 第六個參數:Point偏移量,所有的輪廓信息相對於原始圖像對應點的偏移量,相當於在每一個檢測出的輪廓點上加上該偏移量,並且Point還可以是負值!


函數:drawContours

<code>drawContours(Mat image,

List<matofpoint> contours,

int contourIdx,

Scalar color,

int thickness,

int lineType,

Mat hierarchy,

int maxLevel,

Point offset)/<matofpoint>/<code>


參數介紹

· 第一個參數image表示目標圖像,

· 第二個參數contours表示輸入的輪廓組,每一組輪廓由點vector構成,

· 第三個參數contourIdx指明畫第幾個輪廓,如果該參數為負值,則畫全部輪廓,

· 第四個參數color為輪廓的顏色,

· 第五個參數thickness為輪廓的線寬,如果為負值或CV_FILLED表示填充輪廓內部,

· 第六個參數lineType為線型,

· 第七個參數為輪廓結構信息,

· 第八個參數為maxLevel

· 第九個參數為偏移量


程序步驟

以下是程序的核心步驟:

· 加載本地圖片

<code>String filename = FileLoadUtils.getFilePath("static/ppp3.jpg");

Mat src = Imgcodecs.imread(filename);/<code>


· 灰度變換

<code>//灰度變換

Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY);/<code>

· 濾波處理

<code>//濾波處理

Imgproc.blur(srcGray, srcGray, new Size(3, 3));/<code>

· 邊緣檢測

<code>Mat cannyOutput = new Mat();

Imgproc.Canny(srcGray, cannyOutput, threshold, threshold * 2);

/<code>

· 輪廓檢測


<code>Imgproc.findContours(cannyOutput, contours, hierarchy, retrModel, chainApproxModel);

/<code>

· 輪廓檢測結果的繪畫

<code>Mat drawing = Mat.zeros(cannyOutput.size(), CvType.CV_8UC3);

for (int i = 0; i < contours.size(); i++) {

Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));

Imgproc.drawContours(drawing, contours, i, color, 1, Imgproc.LINE_8, hierarchy, 0, new Point());

}/<code>


代碼

<code>package com.joe.vision.machine.vision.samples;

import org.opencv.core.Point;

import org.opencv.core.*;

import org.opencv.highgui.HighGui;

import org.opencv.imgcodecs.Imgcodecs;

import org.opencv.imgproc.Imgproc;

import javax.swing.*;

import javax.swing.event.ChangeEvent;

import javax.swing.event.ChangeListener;

import java.awt.*;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.FileNotFoundException;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Random;

class FindContours {

private static final int MAX_THRESHOLD = 255;

private Mat srcGray = new Mat();

private JFrame frame;

private JLabel imgSrcLabel;

private JLabel imgContoursLabel;

private int threshold = 100;

private Random rng = new Random(12345);

private static HashMap<string> retrModelMap = new HashMap();

private int retrModel = Imgproc.RETR_TREE;

static {

retrModelMap.put("RETR_EXTERNAL",Imgproc.RETR_EXTERNAL);

retrModelMap.put("RETR_TREE",Imgproc.RETR_TREE);

retrModelMap.put("RETR_LIST",Imgproc.RETR_LIST);

retrModelMap.put("RETR_CCOMP",Imgproc.RETR_CCOMP);

retrModelMap.put("RETR_FLOODFILL",Imgproc.RETR_FLOODFILL);

}

private static HashMap<string> chainApproxMap = new HashMap();

static {

chainApproxMap.put("CHAIN_APPROX_NONE",Imgproc.CHAIN_APPROX_NONE);

chainApproxMap.put("CHAIN_APPROX_SIMPLE",Imgproc.CHAIN_APPROX_SIMPLE);

chainApproxMap.put("CHAIN_APPROX_TC89_L1",Imgproc.CHAIN_APPROX_TC89_L1);

chainApproxMap.put("CHAIN_APPROX_TC89_KCOS",Imgproc.CHAIN_APPROX_TC89_KCOS);

}

private int chainApproxModel = Imgproc.CHAIN_APPROX_SIMPLE;

public FindContours(String[] args) throws FileNotFoundException {

String filename = FileLoadUtils.getFilePath("static/ppp3.jpg");

Mat src = Imgcodecs.imread(filename);

Imgproc.resize(src,src,new Size(src.width(),src.height()));

if (src.empty()) {

System.err.println("Cannot read image: " + filename);

System.exit(0);

}

//灰度變換

Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY);

//濾波處理

Imgproc.blur(srcGray, srcGray, new Size(3, 3));

// Create and set up the window.

frame = new JFrame("Finding contours in your image demo");

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// Set up the content pane.

Image img = HighGui.toBufferedImage(src);

addComponentsToPane(frame.getContentPane(), img);

// Use the content pane's default BorderLayout. No need for

// setLayout(new BorderLayout());

// Display the window.

frame.pack();

frame.setVisible(true);

update();

}

public static void main(String[] args) {

// Load the native OpenCV library

System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

// Schedule a job for the event dispatch thread:

// creating and showing this application's GUI.

javax.swing.SwingUtilities.invokeLater(new Runnable() {

@Override

public void run() {

try {

new FindContours(args);

} catch (FileNotFoundException e) {

e.printStackTrace();

}

}

});

}

private void addComponentsToPane(Container pane, Image img) {

if (!(pane.getLayout() instanceof BorderLayout)) {

pane.add(new JLabel("Container doesn't use BorderLayout!"));

return;

}

JPanel sliderPanel = new JPanel();

sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS));

sliderPanel.add(new JLabel("Canny threshold: "));

//構建滑動工具條

JSlider slider = buildSlider();

//構建檢索模式下拉框

JComboBox<string> retrModelBox = buildRetrModelBox();

//構建鏈近似值模式下拉框

JComboBox<string> chainApproxModelBox = buildChainApproxModelBox();

sliderPanel.add(slider);

sliderPanel.add(new JLabel("輪廓檢索模式"));

sliderPanel.add(retrModelBox);

sliderPanel.add(new JLabel("鏈近似值模式"));


sliderPanel.add(chainApproxModelBox);

pane.add(sliderPanel, BorderLayout.PAGE_START);

JPanel imgPanel = new JPanel();

imgSrcLabel = new JLabel(new ImageIcon(img));

imgPanel.add(imgSrcLabel);

Mat blackImg = Mat.zeros(srcGray.size(), CvType.CV_8U);

imgContoursLabel = new JLabel(new ImageIcon(HighGui.toBufferedImage(blackImg)));

imgPanel.add(imgContoursLabel);

pane.add(imgPanel, BorderLayout.CENTER);

}

private JComboBox<string> buildRetrModelBox() {

JComboBox<string> retrModelBox = new JComboBox(retrModelMap.keySet().toArray());

retrModelBox.addActionListener(new ActionListener() {

@Override

public void actionPerformed(ActionEvent e) {

JComboBox<string> cb = (JComboBox<string>) e.getSource();

retrModel = retrModelMap.get(cb.getSelectedItem());

update();

}

});

return retrModelBox;

}

private JComboBox<string> buildChainApproxModelBox() {

JComboBox<string> retrModelBox = new JComboBox(chainApproxMap.keySet().toArray());

retrModelBox.addActionListener(new ActionListener() {


@Override

public void actionPerformed(ActionEvent e) {

JComboBox<string> cb = (JComboBox<string>) e.getSource();

chainApproxModel = chainApproxMap.get(cb.getSelectedItem());

update();

}

});

return retrModelBox;

}

private JSlider buildSlider() {

JSlider slider = new JSlider(0, MAX_THRESHOLD, threshold);

slider.setMajorTickSpacing(20);

slider.setMinorTickSpacing(10);

slider.setPaintTicks(true);

slider.setPaintLabels(true);

slider.addChangeListener(new ChangeListener() {

@Override

public void stateChanged(ChangeEvent e) {

JSlider source = (JSlider) e.getSource();

threshold = source.getValue();

update();

}

});

return slider;

}


private void update() {

Mat cannyOutput = new Mat();

Imgproc.Canny(srcGray, cannyOutput, threshold, threshold * 2);

List<matofpoint> contours = new ArrayList<>();

Mat hierarchy = new Mat();

Imgproc.findContours(cannyOutput, contours, hierarchy, retrModel, chainApproxModel);

Mat drawing = Mat.zeros(cannyOutput.size(), CvType.CV_8UC3);

for (int i = 0; i < contours.size(); i++) {

Scalar color = new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));

Imgproc.drawContours(drawing, contours, i, color, 1, Imgproc.LINE_8, hierarchy, 0, new Point());

}

imgContoursLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(drawing)));

frame.repaint();

}

}/<matofpoint>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<code>

效果

效果1

輪廓檢索模式為RETR_EXTERNEL,只檢測最外圍輪廓,包含在外圍輪廓內的內圍輪廓被忽略

opencv輪廓findContours&drawContours


效果2

輪廓檢索模式為RETR_TREE,檢索出所有的輪廓

opencv輪廓findContours&drawContours


其他效果操作程序可以看到有所不同


分享到:


相關文章: