com.gitee.huanminabc.multimedia.opencv.OpencvImageASRUtil Maven / Gradle / Ivy
The newest version!
package com.gitee.huanminabc.multimedia.opencv;
import com.alibaba.fastjson.JSON;
import nu.pattern.OpenCV;
import org.json.JSONArray;
import org.json.JSONObject;
import org.opencv.core.*;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.Feature2D;
import org.opencv.features2d.Features2d;
import org.opencv.features2d.ORB;
import org.opencv.imgcodecs.Imgcodecs;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.opencv.imgproc.Imgproc.COLOR_BGR2GRAY;
import static org.opencv.imgproc.Imgproc.cvtColor;
public class OpencvImageASRUtil {
static {
//一起加载
OpenCV.loadShared();
}
//图像特征提取,ORB算法,返回提取的全部特征点
public static String extractFeatures(String inputPath) {
// 读取图像
Mat src = Imgcodecs.imread(inputPath);
// 创建一个ORB特征检测器
Feature2D detector = ORB.create();
// 创建一个MatOfKeyPoint对象,用于存储检测到的特征点
MatOfKeyPoint keypoints = new MatOfKeyPoint();
// 检测特征点
detector.detect(src, keypoints);
// 释放资源
src.release();
//将特征点转换为JSON字符串
return keypointsToJson(keypoints);
}
private static String keypointsToJson(MatOfKeyPoint keypoints) {
//将特征点转换为JSON字符串
JSONArray jsonArray = new JSONArray();
List keyPoints = keypoints.toList();
for (KeyPoint keyPoint : keyPoints) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("x", keyPoint.pt.x);
jsonObject.put("y", keyPoint.pt.y);
jsonObject.put("size", keyPoint.size);
jsonObject.put("angle", keyPoint.angle);
jsonObject.put("response", keyPoint.response);
jsonObject.put("octave", keyPoint.octave);
jsonObject.put("class_id", keyPoint.class_id);
jsonArray.put(jsonObject);
}
return jsonArray.toString();
}
private static MatOfKeyPoint jsonToKeyPoints(String json) {
JSONArray keyPointsJson = new JSONArray(json);
MatOfKeyPoint keypoints = new MatOfKeyPoint();
List keyPoints = new ArrayList<>();
for (int i = 0; i < keyPointsJson.length(); i++) {
JSONObject jsonObject = keyPointsJson.getJSONObject(i);
float x = jsonObject.getFloat("x");
float y = jsonObject.getFloat("y");
float size = jsonObject.getFloat("size");
float angle = jsonObject.getFloat("angle");
float response = jsonObject.getFloat("response");
int octave = jsonObject.getInt("octave");
int class_id = jsonObject.getInt("class_id");
KeyPoint keyPoint = new KeyPoint(x, y, size, angle, response, octave, class_id);
keyPoints.add(keyPoint);
}
keypoints.fromList(keyPoints);
return keypoints;
}
//描述符提取
public static String extractDescriptors(String inputPath, String features) {
// 读取图像
Mat src = Imgcodecs.imread(inputPath);
// 创建一个ORB特征检测器
Feature2D detector = ORB.create();
// 创建一个MatOfKeyPoint对象,用于存储检测到的特征点
MatOfKeyPoint keypoints = jsonToKeyPoints(features);
// 创建一个Mat对象,用于存储描述符
Mat descriptors = new Mat();
// 计算描述符
detector.compute(src, keypoints, descriptors);
// 释放资源
src.release();
// 将描述符转换为JSON字符串
return descriptorsToJson(descriptors);
}
// 开头会添加通道数 ,例如 32###[{"channel_0":[1.0,2.0,3.0],"channel_1":[4.0,5.0,6.0]}] 需要截取掉通道数
private static String descriptorsToJson(Mat descriptors) {
if (descriptors.empty()) {
throw new IllegalArgumentException("The input Mat is empty.");
}
//获取描述符的通道数
int channels = descriptors.channels();
//获取通道类型
int type = descriptors.type();
//将描述符转换为JSON字符串
JSONArray jsonArray = new JSONArray();
for (int i = 0; i < descriptors.rows(); i++) {
JSONObject jsonObject = new JSONObject();
for (int c = 0; c < channels; c++) {
JSONArray channelArray = new JSONArray();
for (int j = 0; j < descriptors.cols(); j++) {
double value = descriptors.get(i, j)[c];
channelArray.put(value);
}
jsonObject.put("channel_" + c, channelArray);
}
jsonArray.put(jsonObject);
}
//在开头添加通道数
Map newJson = new HashMap<>();
newJson.put("channels", channels + "");//通道数
newJson.put("type", type + ""); //类型
newJson.put("json", jsonArray.toString());
return JSON.toJSONString(newJson);
}
//将JSON字符串转换为描述符
private static Mat jsonToDescriptors(String json) {
//截取通道数
Map map = JSON.parseObject(json, Map.class);
int channels = Integer.parseInt(map.get("channels"));
json = map.get("json");
int type = Integer.parseInt(map.get("type"));
//将JSON字符串转换为描述符
JSONArray jsonArray = new JSONArray(json);
int rows = jsonArray.length();
// 假设所有行的长度和通道都相同,从第一行的第一个通道获取列数
int cols = jsonArray.getJSONObject(0).getJSONArray("channel_0").length();
// 创建一个与原始Mat类型相匹配的Mat对象(这里假设是CV_64F类型和给定的通道数)
Mat descriptors = new Mat(rows, cols, type);
for (int i = 0; i < rows; i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
for (int c = 0; c < channels; c++) {
JSONArray channelArray = jsonObject.getJSONArray("channel_" + c);
for (int j = 0; j < cols; j++) {
double value = channelArray.getDouble(j);
// 注意这里要乘以通道数加上当前的通道索引来设置Mat中的位置
descriptors.put(i, j * channels + c, value);
}
}
}
return descriptors;
}
//利用特征点和描述符进行图像匹配
public static boolean match(String originalDescriptors, String matchDescriptors) {
//特征转换
Mat descriptors1 = jsonToDescriptors(originalDescriptors);
Mat descriptors2 = jsonToDescriptors(matchDescriptors);
//创建描述符匹配器
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
//创建一个MatOfDMatch对象,用于存储匹配结果
MatOfDMatch matches = new MatOfDMatch();
//进行匹配
matcher.match(descriptors1, descriptors2, matches);
//释放资源
descriptors1.release();
descriptors2.release();
//计算匹配结果
double max_dist = 0; //最大距离
double min_dist = 100; //最小距离
List matchesList = matches.toList();
for (DMatch match : matchesList) {
double dist = match.distance;
if (dist < min_dist) {
min_dist = dist;
}
if (dist > max_dist) {
max_dist = dist;
}
}
//释放资源
matches.release();
//计算匹配结果
return min_dist < 50; //设置一个阈值,小于50认为匹配成功,相当于一半的特征点匹配成功
}
//将特征点绘制到图像上
public static void drawFeatures(String inputPath, String outputPath, String features) {
// 读取图像
Mat src = Imgcodecs.imread(inputPath);
// 创建一个MatOfKeyPoint对象,用于存储检测到的特征点
MatOfKeyPoint keypoints = jsonToKeyPoints(features);
// 绘制特征点
Features2d.drawKeypoints(src, keypoints, src);
// 保存图像
Imgcodecs.imwrite(outputPath, src);
// 释放资源
src.release();
}
}