欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > 图像编辑器 Monica 之 CV 常见算法的快速调参

图像编辑器 Monica 之 CV 常见算法的快速调参

2025/4/2 14:50:30 来源:https://blog.csdn.net/SLFq6OF5O7aH/article/details/145331768  浏览:    关键词:图像编辑器 Monica 之 CV 常见算法的快速调参

一. 图像编辑器 Monica

Monica 是一款跨平台的桌面图像编辑软件(早期是为了验证一些算法而产生的)。

39dfd09eb26f4941b923290e13720cb4.jpeg
screenshot.png

其技术栈如下:

  • Kotlin 编写 UI(Kotlin Compose Desktop 作为 UI 框架)

  • 基于 mvvm 模式,依赖注入使用 koin,编译使用 JDK 17

  • 部分算法使用 Kotlin 实现

  • 其余的算法使用 OpenCV C++ 来实现,Kotlin 通过 jni 来调用。

  • Monica 所使用的模型,主要使用 ONNXRuntime 进行部署和推理

  • 其余少部分模型使用 OpenCV DNN 进行部署和推理。

  • 本地的算法库使用 C++ 17 编译

Monica 目前还处于开发阶段,当前版本的可以参见 github 地址:https://github.com/fengzhizi715/Monica

这两个月由于工作比较繁忙,我只把 CV 算法快速调参的模块做了完善。

二. 实验性的功能——提供 CV 常见算法的快速调参的能力

在之前的相关系列文章中,曾介绍过该模块完成了二值化、边缘检测、轮廓分析等功能。这次更新了一些新的功能包括: 图像增强、图像降噪、形态学操作、模版匹配。

下面展示该模块的入口f72319ffc6f7258759c1f0b6c7ded9b6.jpeg

以及该模块的首页27064fb7b8eb73dfde9f22cefcdd698e.jpeg

目前,该模块只规划了上述的功能,并已全部实现。短期内暂时不会增加新功能。

2.1 图像增强

该模块提供的图像增强算法,其实之前在 Monica 中早已实现了,我只是把他们从首页移动到这里。这些图像增强算法包括:直方图均衡化、clahe、gamma 变换、Laplace 锐化、USM 锐化、自动色彩均衡。

下图展示的的是进入图像增加的页面,并加载了某个图像的原图。6de93b22b22f965385876d8291268fe5.jpeg

下图分别展示的是原图经过直方图均衡化后的效果和原图经过 gamma 变换后的效果。9b270217a18d71ddfff224795325aa15.jpeg

df5f360955d7ea743b467fa965eb6d99.jpeg
Gamma 变换.png

2.2 图像降噪

该模块提供的图像降噪算法都是通过图像滤波实现的。目前支持高斯滤波、中值滤波、高斯双边滤波、均值迁移滤波。

下图展示的的是进入图像降噪的页面,并加载了某个图像的原图。446bb5bb8486417320e27ebc06ffa28a.jpeg

下图分别展示的是原图经过高斯滤波、高斯双边滤波、均值迁移滤波后的效果。e5fe8214d4470029e5b7cdb055e73937.jpeg

7a1db77f9451e64f8b10ffc5e2e5c19c.jpeg
高斯双边滤波.png
5392a3c5f8f71232615dcf8c871d0f18.jpeg
均值迁移滤波.png

上述功能的实现,分别是调用 OpenCV 对应的图像滤波函数。

2.3 形态学操作

形态学操作是一种基于形状的图像处理技术,通过使用结构元素对图像进行处理,从而提取图像中的形状、大小等信息。

下面用之前的例子,展示一下如何使用形态学操作。首先,加载一张包含多个苹果的图片。

22a1b63c2be9b1075bce383243bf3278.jpeg
加载原图.png

通过彩色图像分割进行二值化。0c8364c875bfcf45ff0e75c0ca9014ba.jpeg

然后再进行形态学的闭操作。2c169bc96bdf154b7336ad44dd459126.jpeg

以及形态学的开操作。0492ceb906db78d9d7c2da997a830e58.jpeg

这些形态学的操作,有助于对轮廓的进一步分析。da8b074b849581b9bfe1bb087a32300d.jpeg

通过轮廓分析之后,我们在原图中可以找到比较明显的苹果。9b611349d826482f3152a17632a4fc00.jpeg

形态学操作的是为了帮助提取图像中主要的信息。

2.4 模版匹配

模板匹配是一种经典的图像处理技术,用于在一幅图像中查找与另一幅模板图像最匹配的部分。

然而 OpenCV 提供的模版匹配函数有一些局限性,例如当模板图像与目标图像之间存在旋转角度差异,或者模板图像与目标图像的尺寸比例不同时,匹配效果都会变差。另外,在默认情况下,模板匹配函数会返回与模板最相似的一个匹配区域,如果需要支持多目标的匹配则需要通过一定的策略来实现。

这里,Monica 实现了一个支持模版匹配的算法,并且支持旋转,用于多角度、多尺度、多目标的模板匹配。

下面,给出简单的演示,先进入支持模版匹配功能的页面。8b590e76c1628d6aebeabbe7ccae93e8.jpeg

加载一张连连看的图片,选取其中一个作为模版图像,并调整一些参数。74637687cdafb86062da60e3a6e46f97.jpeg

最后执行模版匹配,很快能看到匹配的结果。33aa262b00e70e811369de3e34cc216f.jpeg

三. 功能的实现

其他的功能都比较简单,这里只介绍一下模版匹配的实现。由于模版匹配速度很慢,这是使用并行化的模版匹配,以及能够支持多目标的匹配。

class MatchTemplate {public:Mat templateMatching(Mat image, Mat templateImage, int matchType,double angleStart, double angleEnd, double angleStep,double scaleStart, double scaleEnd, double scaleStep,double matchTemplateThreshold,  float scoreThreshold, float nmsThreshold);private:// 使用 Canny 边缘检测Mat computeCanny(const cv::Mat& image, double threshold1, double threshold2);// 处理单个角度和尺度static void processAngleScale(const cv::Mat& inputEdges, const cv::Mat& templateEdges, double angle, double scale,double threshold, std::mutex& resultMutex, std::vector<cv::Rect>& results, std::vector<float>& scores);// 并行化的模板匹配void parallelTemplateMatching(const cv::Mat& inputEdges, const cv::Mat& templateEdges,double angleStart, double angleEnd, double angleStep,double scaleStart, double scaleEnd, double scaleStep,double threshold, std::vector<cv::Rect>& matches, std::vector<float>& scores);// 使用 OpenCV 的 NMSvoid applyNMS(const std::vector<cv::Rect>& boxes, const std::vector<float>& scores, std::vector<cv::Rect>& finalBoxes,float scoreThreshold, float nmsThreshold);
};
#include "../../include/matchTemplate/MatchTemplate.h"usingnamespace cv::dnn;// 使用 Canny 边缘检测
cv::Mat MatchTemplate::computeCanny(const cv::Mat& image, double threshold1 = 50, double threshold2 = 150) {cv::Mat edges;cv::Canny(image, edges, threshold1, threshold2);CV_Assert(edges.type() == CV_8U); // 确保输出为单通道图像return edges;
}// 处理单个角度和尺度
void MatchTemplate::processAngleScale(const cv::Mat& inputEdges, const cv::Mat& templateEdges, double angle, double scale,double threshold, std::mutex& resultMutex, std::vector<cv::Rect>& results, std::vector<float>& scores) {// 旋转模板cv::Point2f center(templateEdges.cols / 2.0f, templateEdges.rows / 2.0f);cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, angle, 1.0);cv::Mat rotatedTemplate;cv::warpAffine(templateEdges, rotatedTemplate, rotationMatrix, templateEdges.size(), cv::INTER_LINEAR);// 缩放模板cv::Mat scaledTemplate;cv::resize(rotatedTemplate, scaledTemplate, cv::Size(), scale, scale);// 检查模板有效性if (scaledTemplate.empty() || scaledTemplate.cols < 1 || scaledTemplate.rows < 1) {return; // 跳过无效模板}// 检查模板与输入图像尺寸if (scaledTemplate.cols > inputEdges.cols || scaledTemplate.rows > inputEdges.rows) {return; // 跳过尺寸不匹配的模板}// 边缘模板匹配cv::Mat result;try {cv::matchTemplate(inputEdges, scaledTemplate, result, cv::TM_CCOEFF_NORMED);} catch (const cv::Exception& e) {std::cerr << "Error in matchTemplate: " << e.what() << std::endl;return;}// 记录满足阈值的匹配结果for (int y = 0; y < result.rows; ++y) {for (int x = 0; x < result.cols; ++x) {float matchScore = result.at<float>(y, x);if (matchScore >= threshold) {std::lock_guard<std::mutex> lock(resultMutex);results.emplace_back(cv::Rect(x, y, scaledTemplate.cols, scaledTemplate.rows));scores.push_back(matchScore);}}}
}// 并行化的模板匹配
void MatchTemplate::parallelTemplateMatching(const cv::Mat& inputEdges, const cv::Mat& templateEdges,double angleStart, double angleEnd, double angleStep,double scaleStart, double scaleEnd, double scaleStep,double threshold, std::vector<cv::Rect>& matches, std::vector<float>& scores) {std::mutex resultMutex;std::vector<std::future<void>> futures;for (double angle = angleStart; angle <= angleEnd; angle += angleStep) {for (double scale = scaleStart; scale <= scaleEnd; scale += scaleStep) {futures.emplace_back(std::async(std::launch::async, &MatchTemplate::processAngleScale,std::ref(inputEdges), std::ref(templateEdges),angle, scale, threshold, std::ref(resultMutex),std::ref(matches), std::ref(scores)));}}// 等待所有线程完成for (auto& future : futures) {future.get();}
}// 使用 OpenCV 的 NMS
void MatchTemplate::applyNMS(const std::vector<cv::Rect>& boxes, const std::vector<float>& scores,std::vector<cv::Rect>& finalBoxes,float scoreThreshold, float nmsThreshold) {if (boxes.empty() || scores.empty()) {return; // 避免空输入导致的崩溃}std::vector<int> indices;NMSBoxes(boxes, scores, scoreThreshold, nmsThreshold, indices);for (int idx : indices) {finalBoxes.push_back(boxes[idx]);}
}Mat MatchTemplate::templateMatching(Mat image, Mat templateImage, int matchType,double angleStart, double angleEnd, double angleStep,double scaleStart, double scaleEnd, double scaleStep,double matchTemplateThreshold,  float scoreThreshold, float nmsThreshold) {// 绘制最终结果cv::Mat resultImage = image.clone();if (matchType == 1) { // 灰度匹配cvtColor(image, image, COLOR_BGR2GRAY);cvtColor(templateImage, templateImage, COLOR_BGR2GRAY);} elseif (matchType == 2) { // 边缘匹配// 计算图像和模板的 Canny 边缘image = computeCanny(image, 50, 150);templateImage = computeCanny(templateImage, 50, 150);}vector<Rect> matches;vector<float> scores;// 并行模板匹配parallelTemplateMatching(image, templateImage, angleStart, angleEnd, angleStep, scaleStart, scaleEnd, scaleStep, matchTemplateThreshold, matches, scores);// 使用 OpenCV 的 NMS 过滤结果vector<Rect> finalMatches;applyNMS(matches, scores, finalMatches, scoreThreshold, nmsThreshold);for (constauto& match : finalMatches) {rectangle(resultImage, match, cv::Scalar(0, 0, 255), 2);}return resultImage;
}

四. 总结

Monica 对 CV 算法快速调参的模块算是基本完成,暂时告一段落。这一模块的后续规划,主要看 2025 年工作的忙碌程度。

Monica 后续的重点是将其现在使用的部分模型,部署到云端以及软件层面 UI 和性能优化等。

Monica github 地址:https://github.com/fengzhizi715/Monica

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词