基于 Compose Desktop 实现的图像编辑器之使用 OpenCV C++ 实现图像增强

一. 图像编辑器 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;
}
e90cd24a7fb9d1a8cfdf10cf5d56799f.jpeg
原图.png
fe920a9eaa2b97421722985f9da2ce0a.jpeg
直方图均衡化后的效果.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]];
    }
}
a1988fb9f322273900bfb184c0833170.jpeg
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);
}
2c9730164b86b4b08c615be61f279e8a.jpeg
原图.png
9d46b907d156babbb559ace49e6a08d5.jpeg
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);
            }
        }
    }
}
01d0e25ea175d2154670b8c0d9c7def0.jpeg
USM 锐化后效果.png

3.5 自动色彩均衡(Automatic Color Equalization)

ACE 的代码量稍微多一些就不贴代码了,感兴趣的可以在 Monica 相关的源码中找到。

9b57b79ddc30d41d2031bf4d94df1077.jpeg
自动色彩均衡后效果.png

换一个图像:6a51c8b4e6657c67abadc451da531d9a.jpeg

98bfba52db396edce024998a88405798.jpeg
自动色彩均衡后效果.png

四. 总结

Monica 目前到了 0.2.6 版本,目前只在 MacOS(Intel芯片)下引入 OpenCV 的编译好的算法库,周末有时间的话我会在 MacOS(m芯片)下编译好的算法库,之后还会在 Windows 平台上编译好算法库。

接下来一段时间的重点是优化软件的架构,引入一些深度学习的模型。

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

该系列的相关文章:

基于 Compose Desktop 实现的图像编辑器之各种形状的裁剪、图像取色、使用查找表实现的滤镜

图像编辑器 Monica 之图像涂鸦、裁剪、有趣的滤镜

使用 Kotlin Compose Desktop 开发的图像编辑器

Java与Android技术栈】公众号

关注 Java/Kotlin 服务端、桌面端 、Android 、机器学习、端侧智能

更多精彩内容请关注:

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值