springboot整合opencv进行灰度图像与RGB图像互转

news2024/11/19 14:35:15

问题:

在开发过程中遇到一个问题,需要在图片上加上数据(原卷留痕),由于图片是灰度的,无法进行彩色编辑,需要将灰度图片转成RGB图片,才能进行彩色编辑,于是想到用opencv进行处理。

参考SpringBoot使用OpenCV总结 - ksfzhaohui的个人页面 - OSCHINA - 中文开源技术交流社区

灰度图片处理前  位深度8 无法进行彩色留痕

 转成RGB图片后 位深度24  可以在上面进行彩色留痕

在开发过程中遇到一些坑

1、最开始我是去OpenCv官网OpenCV - Browse Files at SourceForge.net  下载的文件解压,使用相对路径加载dll文件,在test环境中测试成功了,后来启动springboot服务后,却报错了

Caused by: java.lang.UnsatisfiedLinkError

因为springboot服务会打成jar包运行,通过system.load并不能加载成功;如果使用绝对路径,就可以加载成功

2、由于我们使用的k8s管理微服务,使用绝对路径加载文件肯定不合适,然后参考其他博主的方案,最后采用的方式是把读取的库文件,存放到系统的一个临时文件夹下,然后拿到库文件的绝对路径,这样就可以通过 system.load 直接去加载。

代码如下

package com.zhixinhuixue.opencv;

import java.io.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author Biscop
 * @create 2023-01-05
 * 动态链接库加载类
 */
public class NativeLoader {

    /**
     * 把opencv-460.jar/opencv_java460.dll/opencv_java460.so
     * 放在resources下的opencv文件夹中
     */
    private static final String NATIVE_PATH = "opencv";

    /**
     * 加载native dll/so
     *
     * @throws Exception
     */
    public static void loader() throws Exception {
        Enumeration<URL> dir = Thread.currentThread().getContextClassLoader().getResources(NATIVE_PATH);
        String systemType = System.getProperty("os.name");
        String ext = (systemType.toLowerCase().indexOf("win") != -1) ? ".dll" : ".so";
        while (dir.hasMoreElements()) {
            URL url = dir.nextElement();
            String protocol = url.getProtocol();
            if ("jar".equals(protocol)) {
                JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                JarFile jarFile = jarURLConnection.getJarFile();
                // 遍历Jar包
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry jarEntry = entries.nextElement();
                    String entityName = jarEntry.getName();
                    if (jarEntry.isDirectory() || !entityName.startsWith(NATIVE_PATH)) {
                        continue;
                    }
                    if (entityName.endsWith(ext)) {
                        loadJarNative(jarEntry);
                    }
                }
            } else if ("file".equals(protocol)) {
                File file = new File(url.getPath());
                loadFileNative(file, ext);
            }

        }
    }

    private static void loadFileNative(File file, String ext) {
        if (null == file) {
            return;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (null != files) {
                for (File f : files) {
                    loadFileNative(f, ext);
                }
            }
        }
        if (file.canRead() && file.getName().endsWith(ext)) {
            try {
                System.load(file.getPath());
                System.out.println("加载native文件 :" + file + "成功!!");
            } catch (UnsatisfiedLinkError e) {
                System.out.println("加载native文件 :" + file + "失败!!请确认操作系统是X86还是X64!!!");
            }
        }
    }

    /**
     * @param jarEntry
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private static void loadJarNative(JarEntry jarEntry) throws IOException, ClassNotFoundException {
        File path = new File(".");
        String rootOutputPath = path.getAbsoluteFile().getParent() + File.separator;
        String entityName = jarEntry.getName();
        String fileName = entityName.substring(entityName.lastIndexOf("/") + 1);
        File tempFile = new File(rootOutputPath + File.separator + entityName);
        if (!tempFile.getParentFile().exists()) {
            tempFile.getParentFile().mkdirs();
        }
        if (tempFile.exists()) {
            tempFile.delete();
        }
        InputStream in = null;
        BufferedInputStream reader = null;
        FileOutputStream writer = null;
        try {
            in = NativeLoader.class.getResourceAsStream(entityName);
            if (in == null) {
                in = NativeLoader.class.getResourceAsStream("/" + entityName);
                if (null == in) {
                    return;
                }
            }
            NativeLoader.class.getResource(fileName);
            reader = new BufferedInputStream(in);
            writer = new FileOutputStream(tempFile);
            byte[] buffer = new byte[1024];

            while (reader.read(buffer) > 0) {
                writer.write(buffer);
                buffer = new byte[1024];
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (writer != null) {
                writer.close();
            }
        }
        try {
            System.load(tempFile.getPath());
            System.out.println("加载native文件 :" + tempFile + "成功!!");
        } catch (UnsatisfiedLinkError e) {
            System.out.println("加载native文件 :" + tempFile + "失败!!请确认操作系统是X86还是X64!!!");
        }

    }


}

启动时加载或者使用时加载都行

package com.zhixinhuixue.opencv;

import nu.pattern.OpenCV;
import org.springframework.context.annotation.Configuration;

/**
 * @author Biscop
 * @create 2023-01-05
 */
@Configuration
public class NativeConfig {

    static {
        try {
            NativeLoader.loader();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 引入加载方式
     * <dependency>
     *             <groupId>org.openpnp</groupId>
     *             <artifactId>opencv</artifactId>
     *             <version>4.6.0-0</version>
     *         </dependency>
     */
//    static {
//        OpenCV.loadLocally();
//    }
}

3、引入依赖Maven Central: org.openpnp:opencv:4.6.0-0,已经集成了各个平台的本地库,以及加载本地库的封装类,自己只需要加载就行

调用这个方法OpenCV.loadLocally();
<dependency>
    <groupId>org.openpnp</groupId>
    <artifactId>opencv</artifactId>
    <version>4.6.0-0</version>
</dependency>

我在Linux环境部署时,出现错误 /lib64/libm.so.6: version `GLIBC_2.27' not found,然后我换成3.4.2-2这个版本,但是要注意,有些API会发生变化,要自己去调试。

4、后来和同事讨论,可以通过图片合成的方法,在灰度图像上进行彩色编辑,通过ImageIO就可以实现,无需引入opencv,毕竟这个包很大

//下载图片内容
byte[] data = null;
//处理图片水印
BufferedImage image = null;
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
    Image srcImg = ImageIO.read(new URL(imageUrl));
    int imgWidth = srcImg.getWidth(null);
    int imgHeight = srcImg.getHeight(null);
    logger.info("下载图片{}", imageUrl);
    //创建生成图片(尺寸,色彩)
    image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2d = (Graphics2D) image.getGraphics();
    // 写入原图
    g2d.drawImage(srcImg, 0, 0, null);
    coordinates.forEach(coordinate -> {
        g2d.setColor(coordinate.getColorType() == 1 ? Color.red : Color.green);//画笔颜色
        g2d.setStroke(new BasicStroke(5.0f));
        if (coordinate.getType() == 2) {//正方形
            g2d.drawRect(coordinate.getX(), coordinate.getY(), coordinate.getW(), coordinate.getH());//矩形框(原点x坐标,原点y坐标,矩形的长,矩形的宽)
        } else {
            Font font = new Font("宋体", Font.ITALIC, coordinate.getFontSize());  //水印字体
            g2d.setFont(font); //水印字体
            g2d.drawString(coordinate.getContent(), coordinate.getX(), coordinate.getY()); //水印位置
        }
     });
     g2d.dispose();

     ImageIO.write(image, "jpeg", os);
     data = os.toByteArray();
     logger.info("合成图片数据");
} catch (IOException e) {
    logger.error("原卷编辑失败", e);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/157088.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Grafana 告警模块介绍

Grafana 系列文章&#xff0c;版本&#xff1a;OOS v9.3.1 Grafana 的介绍和安装Grafana监控大屏配置参数介绍&#xff08;一&#xff09;Grafana监控大屏配置参数介绍&#xff08;二&#xff09;Grafana监控大屏可视化图表Grafana 查询数据和转换数据Grafana 告警模块介绍 Gra…

Java对接JeePay支付、转账实现以及回调函数

最近公司对接了第三方支付平台JeePay&#xff0c;看到网上文章比较少&#xff0c;给大家发一篇对接微信支付的吧&#xff0c;支付宝也一样&#xff0c;更换里面的参数即可&#xff0c;官方文档地址&#xff1a;系统介绍 - 计全文档&#xff0c;具体的服务需要大家去搭建&#x…

为ABP新增手机验证模块

当前手机验证基本是标配&#xff0c;但Abp自身并没有实现这个功能&#xff0c;于是有了通过自定义模块实现的想法。 经过研究&#xff0c;发现要实现这个&#xff0c;只要重写和替换包含ReplaceEmailToUsernameOfInputIfNeeds方法的类就可以了。但要实现这个&#xff0c;首先要…

sql server提供三种常用截取字符串方法,LEFT()、RIGHT()、SUBSTRING()

一、sql server提供了三种常用截取字符串方法&#xff0c;LEFT()、RIGHT()、SUBSTRING() 1、LEFT()函数语法&#xff1a;LEFT(character,integer) 注释&#xff1a;参数1&#xff1a;要截取的字符串&#xff0c;参数2&#xff1a;截取字符个数说明&#xff1a;返回从字符串左边…

你的 VS Code 扩展值得信赖吗?

Aqua Nautilus 研究人员最近发现&#xff0c;攻击者可以轻松地冒充流行的 Visual Studio Code 扩展并诱骗不知情的开发人员下载它们。VSCode 是迄今为止最受欢迎的 IDE&#xff1b;StackOverflow 的一项调查指出&#xff0c;其目前已被 74.48% 的开发人员所使用。VSCode 的强大…

SPDK技术浅析

目录SPDK基础知识SPDK架构SPDK使用rpc后台启动基础机制分析后端vhost异步I/O写该篇的来由是因为翻阅到了TriCache: A User-Transparent Block Cache Enabling High-Performance Out-of-Core Processing with In-Memory Programs文章&#xff0c;其中对SPDK的运用的炉火纯青&…

数据结构(1)并查集

(4条消息) 第五课、Trie树、并查集、堆和堆排序_yan__kai_的博客-CSDN博客 活动 - AcWing 并查集作用&#xff1a;一群元素将可以归类到一个代表元素上。可以维护元素到根节点的距离。可以维护每个并查集的大小。 基本操作回顾基础课&#xff0c;特别是“食物链”那道题 目录…

【Django项目开发】部门管理模块的开发(八)

文章目录一、模型类设计二、视图设计1.都有哪些接口三、序列化器类设计四.分页操作1.utils工具中定义pagination.py2.视图类中使用五.路由配置一、模型类设计 一个部门下面可能会有很多子部门&#xff0c;一个子部门上面可能会有父部门&#xff1b;即部门内部之间进行关联&…

国科大模式识别与机器学习2022年期末总结

我根据本学期老师说的考试重点和我自身的情况总结的&#xff0c;希望能帮助到你&#xff0c;如有错误欢迎指正 目录第三章 判别函数Fisher线性判别感知机算法第四章 特征选择和提取K-L变换第五章 统计学习学习基础损失函数风险正则化过拟合欠拟合泛化误差第六章 有监督学习有监…

【jQuery】常用API——jQuery内容文本值

要针对元素的内容还有表单的值操作。 普通元素内容 html()&#xff08;相当于原生 inner HTML) html(); // 获取元素的内容html(内容); // 设置元素的内容<script src"../jquery.min.js"></script> </head><body><div><span>我是…

118页4万字智慧检务大数据平台解决方案

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 目录 第1章 前言 1.1、 政策背…

docker-compose keep-alive mysql8 互为主从

一、准备2台物理机器master-1、master-2&#xff0c;目标虚拟VIP。   VIP:192.168.1.139   master-1:192.168.1.17   master-2:192.168.1.20    二、然后分别在2台物理机器master-1、master-2上使用docker-compose安装mysql8&#xff0c;并配置互为主从。 1&#xff09…

优先级队列、仿函数和反向迭代器

文章目录优先级队列priority_queue的模拟实现框架无参的构造(默认构造)迭代器区间构造向上调整向下调整插入删除取堆顶的数据求数据个数验满初识仿函数模拟实现仿函数更改后的向上调整仿函数更改后的向下调整反向迭代器具体实现优先级队列 1.优先队列是一种容器适配器&#xf…

微信转账api(企业付款)

企业付款介绍 提供企业向用户付款的功能&#xff0c;支持企业通过API接口付款&#xff0c;或通过微信支付商户平台网页功能操作付款。 1. 充值 登录微信支付商户平台&#xff0c;通过网页充值功能充值&#xff08;商户平台-资金管理-现金管理-充值&#xff09;。 温馨提示&a…

BreederDAO x DigiCult AMA——要点总结

问&#xff1a;为什么 BreederDAO 决定花费 200ETH 用于购买 Mythic DigiDaigaku Genesis — Ifrit&#xff1f; 答&#xff1a;除了投资之外&#xff0c;这也是为了确保这个领域中有更多的可触达性&#xff0c;尤其是随着我们 DigiDaigaku 市场工具的推出之后。这也是我们进入…

(十七)Async异步和多线程-语言进阶1

&#xff08;十七&#xff09;Async异步和多线程-语言进阶1一、进程-线程-多线程&#xff0c;同步和异步1.概念2.同步和异步3.异步与多线程异同点二、委托启动异步调用1.同步方法2.异步方法三、多线程的特点四、异步的回调和状态参数1.顺序控制2.状态参数五、异步等待三种方式1…

开学季,护眼灯什么牌子好?2023年护眼台灯推荐

2023年开始了&#xff0c;时间飞逝&#xff0c;而每个父母都越来越紧张自家娃的学业情况&#xff0c;我国近视人数超过7亿人&#xff0c;而儿童时期是视力发育的关键时期&#xff0c;为啥有那么高的近视率呢&#xff1f;主要是用眼过度&#xff0c;以及用眼习惯&#xff0c;而现…

微信小程序——模板与配置,数据绑定,事件绑定

一.数据绑定1.数据绑定的基本原则在data中定义数据在WXML中使用数据2.在data中定义页面的数据在页面对应的.js文件中&#xff0c;把数据定义到data对象中即可3. Mustache 语法的格式把 data 中的数据绑定到页面中渲染&#xff0c;使用 Mustache 语法&#xff08;双大括号&#…

想提高办公效率?可试试java开源工作流框架

在数据化管理越来越规范的当今社会&#xff0c;采用优质的办公软件平台能提高企业的办公协作效率&#xff0c;因而受到了广泛的欢迎和喜爱。那么&#xff0c;什么是java开源工作流框架&#xff1f;我们可以从它的特点、发展前景等方面来加以了解&#xff0c;一起来了解这一产品…

微信公众号运营工具有哪些?赶紧收藏

再厉害的公众号运营大神背后都有一套宝藏工具大全&#xff0c;辅助运营人一路披荆斩棘&#xff0c;堪称神器&#xff01; 我相信网上一搜也能出来很多的运营工具或是网站&#xff0c;但是这里再来给大家来一个大汇总&#xff0c;这次整理绝对是非常详细和实用的&#xff0c;纯…