基于 Compose Desktop 实现的图像编辑器之使用 O
一. 图像编辑器 Monica
Monica 是一款跨平台的桌面图像编辑软件,使用 Kotlin Compose Desktop 作为 UI 框架。
由于应用层是由 Kotlin 编写的,Monica 基于 mvvm 架构,使用 koin 作为依赖注入框架。
部分图像处理算法使用 OpenCV C++ 编写。
Monica 目前还处于开发阶段,当前版本的可以参见 github 地址:https://github.com/fengzhizi715/Monica
二. Kotlin 整合 OpenCV C++
在 Monica 中,有部分算法如果用 Kotlin 来写会太过于复杂而且速度慢。因此,我考虑用 OpenCV 来实现。
Kotlin 可以像 Java 一样通过 jni 调用 C++,下面是 Kotlin 编写调用 jni 层的代码:
object ImageProcess {
init { // 对于不同的平台加载的库是不同的,mac 是 dylib 库,windows 是 dll 库,linux 是 so 库
if (isMac) { // 即使是 mac 系统,针对不同的芯片 也需要加载不同的 dylib 库
System.load("${FileUtil.loadPath}libMonicaImageProcess.dylib")
}
}
/**
* 该算法库的版本号
*/
external fun getVersion():String
/**
* 当前使用的 OpenCV 的版本号
*/
external fun getOpenCVVersion():String
/**
* 直方图均衡化
*/
external fun equalizeHist(src: ByteArray):IntArray
/**
* gamma 校正
*/
external fun gammaCorrection(src: ByteArray,k:Float):IntArray
/**
* laplace 锐化,主要是 8 邻域卷积核
*/
external fun laplace(src: ByteArray):IntArray
/**
* USM 锐化
*/
external fun unsharpMask(src: ByteArray, radius:Int, threshold:Int, amount:Int):IntArray
/**
* 自动色彩均衡
*/
external fun ace(src: ByteArray, ratio:Int, radius:Int):IntArray
}
而对于 jni 层:
cn_netdiscovery_monica_opencv_ImageProcess.h:
#include <jni.h>
#ifndef MONICAIMAGEPROCESS_CN_NETDISCOVERY_MONICA_OPENCV_IMAGEPROCESS_H
#define MONICAIMAGEPROCESS_CN_NETDISCOVERY_MONICA_OPENCV_IMAGEPROCESS_H
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_cn_netdiscovery_monica_opencv_ImageProcess_getVersion
(JNIEnv* env, jobject);
JNIEXPORT jstring JNICALL Java_cn_netdiscovery_monica_opencv_ImageProcess_getOpenCVVersion
(JNIEnv* env, jobject);
JNIEXPORT jintArray JNICALL Java_cn_netdiscovery_monica_opencv_ImageProcess_equalizeHist
(JNIEnv* env, jobject,jbyteArray array);
JNIEXPORT jintArray JNICALL Java_cn_netdiscovery_monica_opencv_ImageProcess_gammaCorrection
(JNIEnv* env, jobject,jbyteArray array, jfloat k);
JNIEXPORT jintArray JNICALL Java_cn_netdiscovery_monica_opencv_ImageProcess_laplace
(JNIEnv* env, jobject,jbyteArray array);
JNIEXPORT jintArray JNICALL Java_cn_netdiscovery_monica_opencv_ImageProcess_unsharpMask
(JNIEnv* env, jobject,jbyteArray array, jint radius, jint threshold, jint amount);
JNIEXPORT jintArray JNICALL Java_cn_netdiscovery_monica_opencv_ImageProcess_ace
(JNIEnv* env, jobject,jbyteArray array, jint ratio, jint radius);
#ifdef __cplusplus
}
#endif
#endif
#pragma once
具体的实现代码就不贴了,感兴趣的可以在 Monica 相关的源码中找到。这里只讲述一下如何将 Kotlin 的 ByteArray 转换成 OpenCV 所使用的 Mat 对象,以及将 Mat 转换成 Kotlin 的 IntArray
Mat byteArrayToMat(JNIEnv* env, jbyteArray array) {
//复制java数组到C++
jsize len = env->GetArrayLength(array);
signed char* pData = new signed char[len];
env->GetByteArrayRegion(array, 0, len, pData);
// 解码内存数据,变成cv::Mat数据
cv::Mat image;
vector<uchar> datas;
for (int i = 0; i < len; ++i) {
datas.push_back(pData[i]);
}
image = cv::imdecode(datas, IMREAD_COLOR);
return image;
}
jintArray matToIntArray(JNIEnv *env, const cv::Mat &image) {
jintArray resultImage = env->NewIntArray(image.total());
jint *_data = new jint[image.total()];
for (int i = 0; i < image.total(); i++) {
char r = image.data[3 * i + 2];
char g = image.data[3 * i + 1];
char b = image.data[3 * i + 0];
char a = (char)255;
_data[i] = (((jint) a << 24) & 0xFF000000) + (((jint) r << 16) & 0x00FF0000) +
(((jint) g << 8) & 0x0000FF00) + ((jint) b & 0x000000FF);
}
env->SetIntArrayRegion(resultImage, 0, image.total(), _data);
delete[]_data;
return resultImage;
}
三. 常用的图像增强算法
这里的大多数算法,在我的<<OpenCV 笔记>>系列中都讲述过或者未来会在那个系列里讲述,所以算法的原理在这里不过多展开。下面只贴一些关键代码和实现的效果图。
3.1 直方图均衡化
直方图均衡化主要的 cv 代码如下:
Mat equalizeHistImage(Mat src) {
int channel = src.channels();
if (channel == 3) {
Mat imageRGB[3];
split(src, imageRGB);
for (int i = 0; i < 3; i++)
{
equalizeHist(imageRGB[i], imageRGB[i]);
}
merge(imageRGB, 3, src);
}
return src;
}
原图.png
直方图均衡化后的效果.png
3.2 Gamma 变换
Gamma 变换主要的 cv 代码如下:
uint8_t gammaLUT[256] = { 0 };
void gammaCorrection(Mat& src, Mat& dst, float K) {
src.copyTo(dst);
for (int i = 0; i < 256; i++) {
float f = i / 255.0f; // 注意不可以写成 i / 255
f = pow(f, K);
gammaLUT[i] = static_cast<uint8_t>(f * 255.0);
}
MatIterator_<Vec3b> it = dst.begin<Vec3b>();
MatIterator_<Vec3b> it_end = dst.end<Vec3b>();
for (; it != it_end; it++)
{
(*it)[0] = gammaLUT[(*it)[0]];
(*it)[1] = gammaLUT[(*it)[1]];
(*it)[2] = gammaLUT[(*it)[2]];
}
}
Gamma 变换后的效果.png
3.3 Laplace 锐化
基于 8 邻域的 Laplace 锐化主要的 cv 代码如下:
void laplace(Mat& src, Mat& dst) {
cv::Mat kernel = (Mat_<char>(3, 3) << -1, -1, -1, -1, 9, -1, -1, -1, -1);
cv::filter2D(src, dst, CV_8UC3, kernel);
}
原图.png
Laplace 锐化后效果.png
3.4 USM 锐化
USM 锐化主要的 cv 代码如下:
void unsharpMask(const Mat& src, Mat& dst, int radius, int threshold, int amount) {
int height = src.rows;
int width = src.cols;
GaussianBlur(src, dst, cv::Size(radius, radius), 2, 2);
for (int h = 0; h < height; ++h) {
for (int w = 0; w < width; ++w) {
int b = src.at<Vec3b>(h, w)[0] - dst.at<Vec3b>(h, w)[0];
int g = src.at<Vec3b>(h, w)[1] - dst.at<Vec3b>(h, w)[1];
int r = src.at<Vec3b>(h, w)[2] - dst.at<Vec3b>(h, w)[2];
if (abs(b) > threshold) {
b = src.at<Vec3b>(h, w)[0] + amount * b / 100;
dst.at<Vec3b>(h, w)[0] = saturate_cast<uchar>(b);
}
if (abs(g) > threshold) {
g = src.at<Vec3b>(h, w)[1] + amount * g / 100;
dst.at<Vec3b>(h, w)[1] = saturate_cast<uchar>(g);
}
if (abs(r) > threshold) {
r = src.at<Vec3b>(h, w)[2] + amount * r / 100;
dst.at<Vec3b>(h, w)[2] = saturate_cast<uchar>(r);
}
}
}
}
USM 锐化后效果.png
3.5 自动色彩均衡(Automatic Color Equalization)
ACE 的代码量稍微多一些就不贴代码了,感兴趣的可以在 Monica 相关的源码中找到。
自动色彩均衡后效果.png换一个图像:
原图.png 自动色彩均衡后效果.png
四. 总结
Monica 目前到了 0.2.6 版本,目前只在 MacOS(Intel芯片)下引入 OpenCV 的编译好的算法库,周末有时间的话我会在 MacOS(m芯片)下编译好的算法库,之后还会在 Windows 平台上编译好算法库。
接下来一段时间的重点是优化软件的架构,引入一些深度学习的模型。
Monica github 地址:https://github.com/fengzhizi715/Monica
该系列的相关文章:
基于 Compose Desktop 实现的图像编辑器之各种形状的裁剪、图像取色、使用查找表实现的滤镜
图像编辑器 Monica 之图像涂鸦、裁剪、有趣的滤镜
使用 Kotlin Compose Desktop 开发的图像编辑器