|
|
|
package com.zhonglai.luhui.smart.feeder.service;
|
|
|
|
|
|
|
|
import com.zhonglai.luhui.smart.feeder.config.OpenCVConfig;
|
|
|
|
import com.zhonglai.luhui.smart.feeder.util.OpenCVUtils;
|
|
|
|
import org.opencv.core.Core;
|
|
|
|
import org.opencv.core.Mat;
|
|
|
|
import com.zhonglai.luhui.smart.feeder.draw.FishRegionPanel;
|
|
|
|
import org.opencv.core.*;
|
|
|
|
import org.opencv.videoio.VideoCapture;
|
|
|
|
import org.opencv.imgproc.Imgproc;
|
|
|
|
import org.opencv.core.MatOfPoint;
|
|
|
|
import org.opencv.core.Scalar;
|
|
|
|
import org.opencv.core.CvType;
|
|
|
|
import org.opencv.core.Rect;
|
|
|
|
|
|
|
|
import javax.swing.*;
|
|
|
|
import java.awt.image.BufferedImage;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
public class OpenCVService {
|
|
|
|
/**
|
|
|
|
* 反光阈值(reflectionThreshold)被设置为100。这意味着所有灰度值低于100的像素都会被设置为0(黑色),灰度值大于或等于100的像素都会被设置为255(白色)。如果你的图像中的对象或区域的灰度值接近或低于这个阈值,它们可能会被排除在二值图像之外。尝试调整这个阈值可能有助于改善结果
|
|
|
|
*/
|
|
|
|
public static int reflectionThreshold = 100; // 反光阈值
|
|
|
|
public static int kernelSize = 3; // 去噪调整内核大小,用来消除小的物体或噪声
|
|
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
OpenCVConfig.loadOpenCv(args);
|
|
|
|
readVideoCaptureForVideo("C:/Users/123/Pictures/图片识别/6月30日.mp4");
|
|
|
|
readVideoCaptureForVideo("C:/Users/123/Pictures/1.mp4");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void readVideoCaptureForVideo(String videoPath)
|
|
|
|
{
|
|
|
|
// 创建VideoCapture对象
|
|
...
|
...
|
@@ -32,86 +34,168 @@ public class OpenCVService { |
|
|
|
System.out.println("无法打开视频文件");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// 背景帧
|
|
|
|
Mat backgroundFrame = new Mat();
|
|
|
|
brightnessIdentifyFishRegion(videoCapture);
|
|
|
|
|
|
|
|
// 释放资源
|
|
|
|
videoCapture.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 亮度查找水面,透明度过滤鱼群
|
|
|
|
*/
|
|
|
|
private static void brightnessIdentifyFishRegion(VideoCapture videoCapture)
|
|
|
|
{
|
|
|
|
// 读取第一帧并获取视频大小
|
|
|
|
Mat previousFrame = new Mat();
|
|
|
|
if (!videoCapture.read(previousFrame)) {
|
|
|
|
System.out.println("无法读取视频帧");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// 获取水域轮廓
|
|
|
|
MatOfPoint largestContour = getDefaultMatOfPoint(previousFrame);
|
|
|
|
|
|
|
|
// 初始阈值范围
|
|
|
|
double minAreaThreshold = Double.MAX_VALUE;
|
|
|
|
double maxAreaThreshold = 0;
|
|
|
|
//画板
|
|
|
|
FishRegionPanel fishRegionPanel = new FishRegionPanel();
|
|
|
|
|
|
|
|
// 逐帧处理视频
|
|
|
|
Mat frame = new Mat();
|
|
|
|
while (videoCapture.read(frame)) {
|
|
|
|
// 背景差分
|
|
|
|
Mat diffFrame = new Mat();
|
|
|
|
Core.absdiff(frame, backgroundFrame, diffFrame);
|
|
|
|
|
|
|
|
// 灰度转换
|
|
|
|
Mat grayFrame = new Mat();
|
|
|
|
Imgproc.cvtColor(diffFrame, grayFrame, Imgproc.COLOR_BGR2GRAY);
|
|
|
|
|
|
|
|
// 阈值处理
|
|
|
|
Mat thresholdFrame = new Mat();
|
|
|
|
Imgproc.threshold(grayFrame, thresholdFrame, 30, 255, Imgproc.THRESH_BINARY);
|
|
|
|
|
|
|
|
// 边缘检测
|
|
|
|
Mat edges = new Mat();
|
|
|
|
Imgproc.Canny(thresholdFrame, edges, 100, 200);
|
|
|
|
|
|
|
|
// 轮廓检测
|
|
|
|
List<MatOfPoint> contours = new ArrayList<>();
|
|
|
|
Mat hierarchy = new Mat();
|
|
|
|
Imgproc.findContours(edges, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
|
|
|
|
|
|
|
|
// 更新阈值范围
|
|
|
|
for (MatOfPoint contour : contours) {
|
|
|
|
double area = Imgproc.contourArea(contour);
|
|
|
|
if (area > maxAreaThreshold) {
|
|
|
|
maxAreaThreshold = area;
|
|
|
|
}
|
|
|
|
if (area < minAreaThreshold) {
|
|
|
|
minAreaThreshold = area;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//抠图
|
|
|
|
Mat shuiyu = matting(frame,largestContour);
|
|
|
|
|
|
|
|
// 根据阈值范围选择适当的阈值
|
|
|
|
double thresholdValue = (maxAreaThreshold + minAreaThreshold) / 2.0;
|
|
|
|
|
|
|
|
// 绘制轮廓
|
|
|
|
Mat contourImage = new Mat(frame.size(), CvType.CV_8UC3, new Scalar(0, 0, 0));
|
|
|
|
Imgproc.drawContours(contourImage, contours, -1, new Scalar(0, 255, 0), 2);
|
|
|
|
|
|
|
|
// 提取鱼群区域
|
|
|
|
for (MatOfPoint contour : contours) {
|
|
|
|
double area = Imgproc.contourArea(contour);
|
|
|
|
if (area > thresholdValue) {
|
|
|
|
// 对于满足面积阈值的轮廓,可以进一步处理或分析
|
|
|
|
// 例如,计算鱼群数量、中心位置等信息
|
|
|
|
// ...
|
|
|
|
|
|
|
|
// 在原图上绘制鱼群区域
|
|
|
|
Rect boundingRect = Imgproc.boundingRect(contour);
|
|
|
|
Imgproc.rectangle(frame, boundingRect.tl(), boundingRect.br(), new Scalar(0, 255, 0), 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 重置阈值范围
|
|
|
|
minAreaThreshold = Double.MAX_VALUE;
|
|
|
|
maxAreaThreshold = 0;
|
|
|
|
// 2. 转换为灰度图像
|
|
|
|
Mat gray = new Mat();
|
|
|
|
Imgproc.cvtColor(shuiyu, gray, Imgproc.COLOR_BGR2GRAY);
|
|
|
|
|
|
|
|
// 3. 进行阈值分割以得到二值图像
|
|
|
|
Mat binaryImage = new Mat();
|
|
|
|
Imgproc.threshold(gray, binaryImage, 100, 255, Imgproc.THRESH_BINARY);
|
|
|
|
|
|
|
|
List<MatOfPoint> contours = new ArrayList<>(); // 用于存储找到的轮廓
|
|
|
|
Mat hierarchy = new Mat(); // 轮廓的层次结构
|
|
|
|
|
|
|
|
// 在水域二值图像中找所有轮廓
|
|
|
|
Imgproc.findContours(binaryImage, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
|
|
|
|
|
|
|
|
//计算大小
|
|
|
|
double area = getArea(contours);
|
|
|
|
|
|
|
|
//标注识别对象
|
|
|
|
Imgproc.drawContours(frame, contours, -1, new Scalar(0, 0, 255), 2);
|
|
|
|
Imgproc.drawContours(frame, Arrays.asList(new MatOfPoint[]{largestContour}), 0, new Scalar(0, 255, 0), 2);
|
|
|
|
|
|
|
|
// 显示图像
|
|
|
|
// 在图像上显示结果
|
|
|
|
displayImage(frame);
|
|
|
|
fishRegionPanel.displayImage(binaryImage);
|
|
|
|
fishRegionPanel.dispSrcImage(frame);
|
|
|
|
// 绘制鱼群变化曲线
|
|
|
|
fishRegionPanel.addFishCount(new Double(area).intValue());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 显示图像
|
|
|
|
private static void displayImage(Mat image) {
|
|
|
|
// 将Mat图像转换为BufferedImage
|
|
|
|
BufferedImage bufferedImage = OpenCVUtils.matToBufferedImage(image);
|
|
|
|
/**
|
|
|
|
* 获取标准水域轮廓
|
|
|
|
* @param previousFrame
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
private static MatOfPoint getDefaultMatOfPoint(Mat previousFrame)
|
|
|
|
{
|
|
|
|
Mat firstBinaryImage = waterBybinary(previousFrame);
|
|
|
|
// 绘制白色区域的轮廓
|
|
|
|
List<MatOfPoint> contours = new ArrayList<>();
|
|
|
|
Mat hierarchy = new Mat();
|
|
|
|
Imgproc.findContours(firstBinaryImage, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
|
|
|
|
// 找到最大区域
|
|
|
|
double maxArea = 0;
|
|
|
|
int maxAreaIndex = -1;
|
|
|
|
for (int i = 0; i < contours.size(); i++) {
|
|
|
|
double area = Imgproc.contourArea(contours.get(i));
|
|
|
|
if (area > maxArea) {
|
|
|
|
maxArea = area;
|
|
|
|
maxAreaIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 获取最大区域的轮廓
|
|
|
|
MatOfPoint largestContour = contours.get(maxAreaIndex);
|
|
|
|
return largestContour;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static double getArea(List<MatOfPoint> contours) {
|
|
|
|
// 找到最大区域
|
|
|
|
double maxArea = 0;
|
|
|
|
int maxAreaIndex = -1;
|
|
|
|
for (int i = 0; i < contours.size(); i++) {
|
|
|
|
double area = Imgproc.contourArea(contours.get(i));
|
|
|
|
if (area > maxArea) {
|
|
|
|
maxArea = area;
|
|
|
|
maxAreaIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(-1 != maxAreaIndex)
|
|
|
|
{
|
|
|
|
contours.remove(maxAreaIndex);
|
|
|
|
}
|
|
|
|
// 返回总面积
|
|
|
|
return maxArea;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 在标签上显示图像
|
|
|
|
new ImageIcon(bufferedImage);
|
|
|
|
|
|
|
|
// 更新窗口
|
|
|
|
private static Mat matting(Mat frame,MatOfPoint largestContour)
|
|
|
|
{
|
|
|
|
// 创建一个与原始图像相同大小的新Mat,用于提取图像区域
|
|
|
|
Mat extractedRegion = Mat.zeros(frame.size(), frame.type());
|
|
|
|
|
|
|
|
// 将指定的轮廓绘制到新的Mat上
|
|
|
|
Imgproc.drawContours(extractedRegion, Collections.singletonList(largestContour), 0, new Scalar(255, 255, 255), -1);
|
|
|
|
|
|
|
|
// 使用按位与操作提取对应的图像区域
|
|
|
|
Mat extractedImage = new Mat();
|
|
|
|
Core.bitwise_and(frame, extractedRegion, extractedImage);
|
|
|
|
|
|
|
|
return extractedImage;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 根据反光查找水面
|
|
|
|
* @param frame
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
private static Mat waterBybinary(Mat frame) {
|
|
|
|
// 将加载的图像转换为灰度图像,以便进行亮度或反光的分析
|
|
|
|
Mat grayImage = new Mat();
|
|
|
|
Imgproc.cvtColor(frame, grayImage, Imgproc.COLOR_BGR2GRAY);
|
|
|
|
|
|
|
|
// 检测反光
|
|
|
|
Mat binaryImage = new Mat();
|
|
|
|
double maxValue = 255;
|
|
|
|
Imgproc.threshold(grayImage, binaryImage, reflectionThreshold, maxValue, Imgproc.THRESH_BINARY);
|
|
|
|
|
|
|
|
// 进行形态学操作,去除噪点
|
|
|
|
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(kernelSize, kernelSize));
|
|
|
|
Imgproc.morphologyEx(binaryImage, binaryImage, Imgproc.MORPH_OPEN, kernel);
|
|
|
|
|
|
|
|
return binaryImage;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static List<MatOfPoint> fishByWater(Mat frame)
|
|
|
|
{
|
|
|
|
// 2. 转换为灰度图像
|
|
|
|
Mat gray = new Mat();
|
|
|
|
Imgproc.cvtColor(frame, gray, Imgproc.COLOR_BGR2GRAY);
|
|
|
|
|
|
|
|
// 3. 进行阈值分割以得到二值图像
|
|
|
|
Mat binary = new Mat();
|
|
|
|
Imgproc.threshold(gray, binary, 100, 255, Imgproc.THRESH_BINARY);
|
|
|
|
|
|
|
|
// 4. 查找轮廓
|
|
|
|
List<MatOfPoint> contours = new ArrayList<>();
|
|
|
|
Mat hierarchy = new Mat();
|
|
|
|
Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
|
|
|
|
|
|
|
|
return contours;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} |
...
|
...
|
|