|
|
|
|
@ -389,5 +389,440 @@ public class OpencvService {
|
|
|
|
|
clahe.release();
|
|
|
|
|
return binary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ==================== 图像融合 ====================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 1. 加权融合 - 两张图按透明度线性混合
|
|
|
|
|
* result = img1 * (1-alpha) + img2 * alpha
|
|
|
|
|
*
|
|
|
|
|
* @param img1Path 底图路径
|
|
|
|
|
* @param img2Path 叠加图路径
|
|
|
|
|
* @param destPath 输出路径
|
|
|
|
|
* @param alpha 融合权重(0~1),alpha越大img2越明显
|
|
|
|
|
*/
|
|
|
|
|
public void blendWeighted(String img1Path, String img2Path, String destPath, double alpha) {
|
|
|
|
|
Mat img1 = Imgcodecs.imread(img1Path);
|
|
|
|
|
Mat img2 = Imgcodecs.imread(img2Path);
|
|
|
|
|
|
|
|
|
|
if (img1.empty() || img2.empty()) {
|
|
|
|
|
log.error("读取图片失败");
|
|
|
|
|
closeMats(img1, img2);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 统一尺寸为img1
|
|
|
|
|
if (img2.size() != img1.size()) {
|
|
|
|
|
Imgproc.resize(img2, img2, img1.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mat result = blendWeighted(img1, img2, alpha);
|
|
|
|
|
Imgcodecs.imwrite(destPath, result);
|
|
|
|
|
log.info("加权融合完成,alpha={}, 输出: {}", alpha, destPath);
|
|
|
|
|
|
|
|
|
|
closeMats(result, img1, img2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 加权融合 Mat 版本
|
|
|
|
|
*/
|
|
|
|
|
public Mat blendWeighted(Mat img1, Mat img2, double alpha) {
|
|
|
|
|
Mat result = new Mat();
|
|
|
|
|
Core.addWeighted(img1, 1.0 - alpha, img2, alpha, 0, result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 2. ROI 区域融合 - 将小图贴到大图的指定位置(支持透明蒙版)
|
|
|
|
|
*
|
|
|
|
|
* @param bigPath 大图路径
|
|
|
|
|
* @param smallPath 小图路径
|
|
|
|
|
* @param destPath 输出路径
|
|
|
|
|
* @param x 贴入位置X
|
|
|
|
|
* @param y 贴入位置Y
|
|
|
|
|
* @param alpha 融合透明度(0=完全不透明覆盖, 1=完全透明)
|
|
|
|
|
*/
|
|
|
|
|
public void blendRoi(String bigPath, String smallPath, String destPath, int x, int y, double alpha) {
|
|
|
|
|
Mat big = Imgcodecs.imread(bigPath);
|
|
|
|
|
Mat small = Imgcodecs.imread(smallPath);
|
|
|
|
|
|
|
|
|
|
if (big.empty() || small.empty()) {
|
|
|
|
|
log.error("读取图片失败");
|
|
|
|
|
closeMats(big, small);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mat result = blendRoi(big, small, x, y, alpha);
|
|
|
|
|
Imgcodecs.imwrite(destPath, result);
|
|
|
|
|
log.info("ROI融合完成,位置({},{}), 输出: {}", x, y, destPath);
|
|
|
|
|
|
|
|
|
|
closeMats(result, big, small);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ROI 区域融合 Mat 版本
|
|
|
|
|
*/
|
|
|
|
|
public Mat blendRoi(Mat big, Mat small, int x, int y, double alpha) {
|
|
|
|
|
// 裁剪超界部分
|
|
|
|
|
int w = Math.min(small.cols(), big.cols() - x);
|
|
|
|
|
int h = Math.min(small.rows(), big.rows() - y);
|
|
|
|
|
if (w <= 0 || h <= 0) {
|
|
|
|
|
log.error("小图超出大图范围");
|
|
|
|
|
return big.clone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mat roi = big.submat(new Rect(x, y, w, h));
|
|
|
|
|
Mat smallCrop = small.submat(new Rect(0, 0, w, h));
|
|
|
|
|
|
|
|
|
|
if (alpha > 0) {
|
|
|
|
|
// 半透明融合
|
|
|
|
|
Core.addWeighted(roi, alpha, smallCrop, 1.0 - alpha, 0, roi);
|
|
|
|
|
} else {
|
|
|
|
|
// 直接覆盖
|
|
|
|
|
smallCrop.copyTo(roi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return big.clone();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 3. 蒙版融合 - 通过mask控制融合区域
|
|
|
|
|
* mask中白色区域显示img2,黑色区域显示img1
|
|
|
|
|
*
|
|
|
|
|
* @param img1Path 底图路径
|
|
|
|
|
* @param img2Path 叠加图路径
|
|
|
|
|
* @param maskPath 蒙版路径(黑白图,白色=显示img2)
|
|
|
|
|
* @param destPath 输出路径
|
|
|
|
|
*/
|
|
|
|
|
public void blendByMask(String img1Path, String img2Path, String maskPath, String destPath) {
|
|
|
|
|
Mat img1 = Imgcodecs.imread(img1Path);
|
|
|
|
|
Mat img2 = Imgcodecs.imread(img2Path);
|
|
|
|
|
Mat mask = Imgcodecs.imread(maskPath, Imgcodecs.IMREAD_GRAYSCALE);
|
|
|
|
|
|
|
|
|
|
if (img1.empty() || img2.empty() || mask.empty()) {
|
|
|
|
|
log.error("读取图片失败");
|
|
|
|
|
closeMats(img1, img2, mask);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 统一尺寸
|
|
|
|
|
Size size = img1.size();
|
|
|
|
|
if (!img2.size().equals(size)) Imgproc.resize(img2, img2, size);
|
|
|
|
|
if (!mask.size().equals(size)) Imgproc.resize(mask, mask, size);
|
|
|
|
|
|
|
|
|
|
Mat result = blendByMask(img1, img2, mask);
|
|
|
|
|
Imgcodecs.imwrite(destPath, result);
|
|
|
|
|
log.info("蒙版融合完成,输出: {}", destPath);
|
|
|
|
|
|
|
|
|
|
closeMats(result, img1, img2, mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 蒙版融合 Mat 版本
|
|
|
|
|
*/
|
|
|
|
|
public Mat blendByMask(Mat img1, Mat img2, Mat mask) {
|
|
|
|
|
// 归一化mask到0~1
|
|
|
|
|
Mat maskNorm = new Mat();
|
|
|
|
|
mask.convertTo(maskNorm, CvType.CV_32FC1, 1.0 / 255.0);
|
|
|
|
|
|
|
|
|
|
// 分别取img1和img2各通道
|
|
|
|
|
Mat result = new Mat(img1.size(), img1.type());
|
|
|
|
|
for (int c = 0; c < img1.channels(); c++) {
|
|
|
|
|
Core.multiply(img1, Scalar.all(1.0), result); // 先全取img1...简化处理,直接像素级混合
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 浮点运算: result = img1 * (1-mask) + img2 * mask
|
|
|
|
|
Mat img1f = new Mat();
|
|
|
|
|
Mat img2f = new Mat();
|
|
|
|
|
img1.convertTo(img1f, CvType.CV_32FC3);
|
|
|
|
|
img2.convertTo(img2f, CvType.CV_32FC3);
|
|
|
|
|
|
|
|
|
|
Mat resultF = new Mat();
|
|
|
|
|
// resultF = img2 * mask
|
|
|
|
|
List<Mat> maskChannels = new ArrayList<>();
|
|
|
|
|
for (int i = 0; i < 3; i++) maskChannels.add(maskNorm);
|
|
|
|
|
Mat mask3 = new Mat();
|
|
|
|
|
Core.merge(maskChannels, mask3);
|
|
|
|
|
Core.multiply(img2f, mask3, resultF);
|
|
|
|
|
|
|
|
|
|
// resultF += img1 * (1-mask)
|
|
|
|
|
Mat invMask = new Mat();
|
|
|
|
|
// Core.subtract(Scalar.all(1.0), mask3, invMask);
|
|
|
|
|
Mat img1Masked = new Mat();
|
|
|
|
|
Core.multiply(img1f, invMask, img1Masked);
|
|
|
|
|
Core.add(resultF, img1Masked, resultF);
|
|
|
|
|
|
|
|
|
|
resultF.convertTo(result, CvType.CV_8UC3);
|
|
|
|
|
|
|
|
|
|
// closeMats(maskNorm, mask3, invMask, img1Masked, resultF, img1f, img2f, maskChannels.toArray(new Mat[0]));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 4. 拉普拉斯金字塔融合 - 多尺度融合,边缘过渡自然
|
|
|
|
|
* 适合将两张图无缝拼接,接缝处平滑过渡
|
|
|
|
|
*
|
|
|
|
|
* @param img1Path 左侧/上方图路径
|
|
|
|
|
* @param img2Path 右侧/下方图路径
|
|
|
|
|
* @param destPath 输出路径
|
|
|
|
|
* @param levels 金字塔层数(推荐4~6)
|
|
|
|
|
* @param horizontal true=水平拼接融合, false=垂直拼接融合
|
|
|
|
|
*/
|
|
|
|
|
public void blendPyramid(String img1Path, String img2Path, String destPath, int levels, boolean horizontal) {
|
|
|
|
|
Mat img1 = Imgcodecs.imread(img1Path);
|
|
|
|
|
Mat img2 = Imgcodecs.imread(img2Path);
|
|
|
|
|
|
|
|
|
|
if (img1.empty() || img2.empty()) {
|
|
|
|
|
log.error("读取图片失败");
|
|
|
|
|
closeMats(img1, img2);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 统一尺寸(按较大者)
|
|
|
|
|
int h = Math.max(img1.rows(), img2.rows());
|
|
|
|
|
int w = Math.max(img1.cols(), img2.cols());
|
|
|
|
|
if (img1.rows() != h || img1.cols() != w) Imgproc.resize(img1, img1, new Size(w, h));
|
|
|
|
|
if (img2.rows() != h || img2.cols() != w) Imgproc.resize(img2, img2, new Size(w, h));
|
|
|
|
|
|
|
|
|
|
Mat result = blendPyramid(img1, img2, levels, horizontal);
|
|
|
|
|
Imgcodecs.imwrite(destPath, result);
|
|
|
|
|
log.info("金字塔融合完成,levels={}, 输出: {}", levels, destPath);
|
|
|
|
|
|
|
|
|
|
closeMats(result, img1, img2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 拉普拉斯金字塔融合 Mat 版本
|
|
|
|
|
*/
|
|
|
|
|
public Mat blendPyramid(Mat img1, Mat img2, int levels, boolean horizontal) {
|
|
|
|
|
// 确保是3通道
|
|
|
|
|
Mat i1 = img1.channels() == 3 ? img1.clone() : new Mat();
|
|
|
|
|
Mat i2 = img2.channels() == 3 ? img2.clone() : new Mat();
|
|
|
|
|
if (img1.channels() == 1) Imgproc.cvtColor(img1, i1, Imgproc.COLOR_GRAY2BGR);
|
|
|
|
|
if (img2.channels() == 1) Imgproc.cvtColor(img2, i2, Imgproc.COLOR_GRAY2BGR);
|
|
|
|
|
|
|
|
|
|
// 构建蒙版:一半白一半黑
|
|
|
|
|
Mat mask = Mat.zeros(i1.size(), CvType.CV_8UC1);
|
|
|
|
|
if (horizontal) {
|
|
|
|
|
Imgproc.rectangle(mask, new Rect(0, 0, i1.cols() / 2, i1.rows()), new Scalar(255), -1);
|
|
|
|
|
} else {
|
|
|
|
|
Imgproc.rectangle(mask, new Rect(0, 0, i1.cols(), i1.rows() / 2), new Scalar(255), -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构建高斯金字塔(mask平滑版本)
|
|
|
|
|
Mat maskFloat = new Mat();
|
|
|
|
|
mask.convertTo(maskFloat, CvType.CV_32FC1, 1.0 / 255.0);
|
|
|
|
|
Mat maskBlur = new Mat();
|
|
|
|
|
Imgproc.GaussianBlur(maskFloat, maskBlur, new Size(31, 31), 10);
|
|
|
|
|
|
|
|
|
|
// 三通道mask
|
|
|
|
|
List<Mat> mc = new ArrayList<>();
|
|
|
|
|
for (int i = 0; i < 3; i++) mc.add(maskBlur);
|
|
|
|
|
Mat mask3 = new Mat();
|
|
|
|
|
Core.merge(mc, mask3);
|
|
|
|
|
|
|
|
|
|
// 拉普拉斯金字塔:对每一层做下采样-上采样-相减得到高频细节
|
|
|
|
|
Mat i1f = new Mat(), i2f = new Mat();
|
|
|
|
|
i1.convertTo(i1f, CvType.CV_32FC3, 1.0 / 255.0);
|
|
|
|
|
i2.convertTo(i2f, CvType.CV_32FC3, 1.0 / 255.0);
|
|
|
|
|
|
|
|
|
|
List<Mat> pyr1 = buildLaplacianPyramid(i1f, levels);
|
|
|
|
|
List<Mat> pyr2 = buildLaplacianPyramid(i2f, levels);
|
|
|
|
|
List<Mat> pyrMask = buildGaussianPyramid(mask3, levels);
|
|
|
|
|
|
|
|
|
|
// 每层融合
|
|
|
|
|
List<Mat> pyrResult = new ArrayList<>();
|
|
|
|
|
for (int i = 0; i < levels; i++) {
|
|
|
|
|
Mat layer = new Mat();
|
|
|
|
|
// Core.add(
|
|
|
|
|
// pyr2.get(i).mul(pyrMask.get(i)),
|
|
|
|
|
// pyr1.get(i).mul(Scalar.all(1.0)).sub(pyr1.get(i).mul(pyrMask.get(i))),
|
|
|
|
|
// // 简化:直接mask混合
|
|
|
|
|
// layer
|
|
|
|
|
// );
|
|
|
|
|
// 更准确的写法
|
|
|
|
|
Mat term1 = new Mat(), term2 = new Mat();
|
|
|
|
|
Core.multiply(pyr2.get(i), pyrMask.get(i), term1);
|
|
|
|
|
Mat invMask = new Mat();
|
|
|
|
|
// Core.subtract(Scalar.all(1.0), pyrMask.get(i), invMask);
|
|
|
|
|
Core.multiply(pyr1.get(i), invMask, term2);
|
|
|
|
|
Core.add(term1, term2, layer);
|
|
|
|
|
pyrResult.add(layer);
|
|
|
|
|
|
|
|
|
|
closeMats(term1, term2, invMask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 重建
|
|
|
|
|
Mat result = reconstructFromPyramid(pyrResult);
|
|
|
|
|
|
|
|
|
|
result.convertTo(result, CvType.CV_8UC3, 255.0);
|
|
|
|
|
|
|
|
|
|
// 释放金字塔
|
|
|
|
|
pyr1.forEach(Mat::release);
|
|
|
|
|
pyr2.forEach(Mat::release);
|
|
|
|
|
pyrMask.forEach(Mat::release);
|
|
|
|
|
pyrResult.forEach(Mat::release);
|
|
|
|
|
closeMats(mask, maskFloat, maskBlur, mask3, i1f, i2f, i1, i2);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 5. 多频段融合 (Multi-Band Blending) - 更高质量的拼接融合
|
|
|
|
|
* 适合全景图拼接、两张不同曝光图融合等场景
|
|
|
|
|
*
|
|
|
|
|
* @param img1Path 底图路径
|
|
|
|
|
* @param img2Path 叠加图路径
|
|
|
|
|
* @param destPath 输出路径
|
|
|
|
|
* @param bands 频段数(推荐4~8)
|
|
|
|
|
*/
|
|
|
|
|
public void blendMultiBand(String img1Path, String img2Path, String destPath, int bands) {
|
|
|
|
|
Mat img1 = Imgcodecs.imread(img1Path);
|
|
|
|
|
Mat img2 = Imgcodecs.imread(img2Path);
|
|
|
|
|
|
|
|
|
|
if (img1.empty() || img2.empty()) {
|
|
|
|
|
log.error("读取图片失败");
|
|
|
|
|
closeMats(img1, img2);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 统一尺寸
|
|
|
|
|
if (!img1.size().equals(img2.size())) {
|
|
|
|
|
Imgproc.resize(img2, img2, img1.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Mat result = blendMultiBand(img1, img2, bands);
|
|
|
|
|
Imgcodecs.imwrite(destPath, result);
|
|
|
|
|
log.info("多频段融合完成,bands={}, 输出: {}", bands, destPath);
|
|
|
|
|
|
|
|
|
|
closeMats(result, img1, img2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 多频段融合 Mat 版本
|
|
|
|
|
*/
|
|
|
|
|
public Mat blendMultiBand(Mat img1, Mat img2, int bands) {
|
|
|
|
|
// 转3通道
|
|
|
|
|
Mat i1 = toBgr(img1);
|
|
|
|
|
Mat i2 = toBgr(img2);
|
|
|
|
|
|
|
|
|
|
// 高斯金字塔 smooth 后连加
|
|
|
|
|
List<Mat> gauss1 = new ArrayList<>();
|
|
|
|
|
List<Mat> gauss2 = new ArrayList<>();
|
|
|
|
|
Mat current = i1.clone();
|
|
|
|
|
gauss1.add(current.clone());
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < bands; i++) {
|
|
|
|
|
Mat down = new Mat();
|
|
|
|
|
Imgproc.pyrDown(current, down);
|
|
|
|
|
current = down;
|
|
|
|
|
gauss1.add(current.clone());
|
|
|
|
|
}
|
|
|
|
|
closeMats(current);
|
|
|
|
|
|
|
|
|
|
current = i2.clone();
|
|
|
|
|
gauss2.add(current.clone());
|
|
|
|
|
for (int i = 0; i < bands; i++) {
|
|
|
|
|
Mat down = new Mat();
|
|
|
|
|
Imgproc.pyrDown(current, down);
|
|
|
|
|
current = down;
|
|
|
|
|
gauss2.add(current.clone());
|
|
|
|
|
}
|
|
|
|
|
closeMats(current);
|
|
|
|
|
|
|
|
|
|
// 重建最高层
|
|
|
|
|
Mat topLayer = new Mat();
|
|
|
|
|
Core.addWeighted(gauss1.get(bands), 0.5, gauss2.get(bands), 0.5, 0, topLayer);
|
|
|
|
|
|
|
|
|
|
Mat result = topLayer;
|
|
|
|
|
for (int i = bands - 1; i >= 0; i--) {
|
|
|
|
|
Mat up = new Mat();
|
|
|
|
|
Imgproc.pyrUp(result, up, gauss1.get(i).size());
|
|
|
|
|
Mat blended = new Mat();
|
|
|
|
|
Core.addWeighted(gauss1.get(i), 0.5, gauss2.get(i), 0.5, 0, blended);
|
|
|
|
|
Core.add(up, blended, result = new Mat());
|
|
|
|
|
closeMats(up, blended);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gauss1.forEach(Mat::release);
|
|
|
|
|
gauss2.forEach(Mat::release);
|
|
|
|
|
closeMats(i1, i2);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ==================== 融合辅助方法 ====================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 构建高斯金字塔
|
|
|
|
|
*/
|
|
|
|
|
private List<Mat> buildGaussianPyramid(Mat src, int levels) {
|
|
|
|
|
List<Mat> pyramid = new ArrayList<>();
|
|
|
|
|
Mat current = src.clone();
|
|
|
|
|
pyramid.add(current);
|
|
|
|
|
for (int i = 1; i < levels; i++) {
|
|
|
|
|
Mat down = new Mat();
|
|
|
|
|
Imgproc.pyrDown(current, down);
|
|
|
|
|
pyramid.add(down);
|
|
|
|
|
current = down;
|
|
|
|
|
}
|
|
|
|
|
return pyramid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 构建拉普拉斯金字塔
|
|
|
|
|
*/
|
|
|
|
|
private List<Mat> buildLaplacianPyramid(Mat src, int levels) {
|
|
|
|
|
List<Mat> gauss = buildGaussianPyramid(src, levels);
|
|
|
|
|
List<Mat> laplace = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < levels - 1; i++) {
|
|
|
|
|
Mat up = new Mat();
|
|
|
|
|
Imgproc.pyrUp(gauss.get(i + 1), up, gauss.get(i).size());
|
|
|
|
|
Mat lap = new Mat();
|
|
|
|
|
Core.subtract(gauss.get(i), up, lap);
|
|
|
|
|
laplace.add(lap);
|
|
|
|
|
closeMats(up);
|
|
|
|
|
}
|
|
|
|
|
laplace.add(gauss.get(levels - 1).clone());
|
|
|
|
|
|
|
|
|
|
gauss.forEach(Mat::release);
|
|
|
|
|
return laplace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 从金字塔重建原图
|
|
|
|
|
*/
|
|
|
|
|
private Mat reconstructFromPyramid(List<Mat> pyramid) {
|
|
|
|
|
Mat result = pyramid.get(pyramid.size() - 1).clone();
|
|
|
|
|
for (int i = pyramid.size() - 2; i >= 0; i--) {
|
|
|
|
|
Mat up = new Mat();
|
|
|
|
|
Imgproc.pyrUp(result, up, pyramid.get(i).size());
|
|
|
|
|
Mat sum = new Mat();
|
|
|
|
|
Core.add(up, pyramid.get(i), sum);
|
|
|
|
|
result = sum;
|
|
|
|
|
closeMats(up);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 转为BGR三通道
|
|
|
|
|
*/
|
|
|
|
|
private Mat toBgr(Mat src) {
|
|
|
|
|
if (src.channels() == 3) return src.clone();
|
|
|
|
|
Mat bgr = new Mat();
|
|
|
|
|
Imgproc.cvtColor(src, bgr, Imgproc.COLOR_GRAY2BGR);
|
|
|
|
|
return bgr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 批量释放 Mat
|
|
|
|
|
*/
|
|
|
|
|
private void closeMats(Mat... mats) {
|
|
|
|
|
for (Mat m : mats) {
|
|
|
|
|
if (m != null) m.release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|