python的opencv操作记录(十)——图像融合

news2024/11/29 2:35:37

文章目录

  • 前言
  • opencv中的一个方法
  • 泊松融合
    • 图像梯度
    • 图像散度
    • 融合图像散度
    • 通过散度场进行图像重建
  • 泊松融合的一般逻辑

前言

最近碰到一个项目上的难题,是要从电动显微镜对焦的多张图像进行融合。因为,显微镜物镜的景深范围较小,可能在同一视野中有多个需要拍摄的物体位于不同的景深范围内,所以想通过图像的融合,将不同景深上的多张图像进行融合,从而把这些物体都在同一张图像中对用户进行展示。

opencv中的一个方法

在opencv中,提供了一个图像融合函数seamlessClone,可以直接进行图像融合。

    dst = cv2.imread("/Users/zoulei/files/personal/images/bg.jpg")
    obj = cv2.imread("/Users/zoulei/files/personal/images/fore.jpg")

    # Create an all white mask
    mask = 255 * np.ones(obj.shape, obj.dtype)

    # The location of the center of the src in the dst
    width, height, channels = dst.shape
    center = (int(height / 2), int(width / 2))

    # Seamlessly clone src into dst and put the results in output
    normal_clone = cv2.seamlessClone(obj, dst, mask, center, cv2.NORMAL_CLONE)
    mixed_clone = cv2.seamlessClone(obj, dst, mask, center, cv2.MIXED_CLONE)

    cv2.imshow("origin image", dst)
    cv2.imshow("fution image normal", normal_clone)
    cv2.imshow("fution image mixed", mixed_clone)

    cv2.waitKey()
    cv2.destroyAllWindows()

在这里插入图片描述

opencv这块的原理应该是基于一个牛逼的图像融合算法:泊松融合,出自于论文[Poisson Image Editing-2003]。于是去了解了一下这个厉害的算法。

泊松融合

泊松融合大概的逻辑是:

  • 将两幅图像都计算散度场,然后将两幅图像的散度场通过掩码进行相加
  • 通过计算泊松方程重建图像。

我自己的理解是求散度之后更好的得到图像更深层的信息,在更深的层次上对图像进行信息融合,然后再重建,比直接了当的把图贴上去效果要好得多。

当然,论文是比较严谨和难懂的,我这里只是大概的记录下过程,以便于理解。

图像梯度

图像梯度的概念在我之前计算清晰度的文章中已经描述过了:
https://blog.csdn.net/pcgamer/article/details/127942102?spm=1001.2014.3001.5502
就不再赘述了。

图像散度

图像的散度的定义就是对图像进行二阶求导,实际上就是拉普拉斯算子:
∇ 2 f = ∂ 2 f ∂ 2 x + ∂ 2 y ∂ 2 x \nabla^2f=\frac{\partial^2 f}{\partial^2 x} + \frac{\partial^2 y}{\partial^2 x} 2f=2x2f+2x2y

因为图像是离散的数据,所以这个偏导(x方向上)的在离散数据上的定义就是:
∂ 2 f ∂ 2 x = f ( x + 1 , y ) + f ( x − 1 , y ) − 2 f ( x , y ) \frac{\partial^2 f}{\partial^2 x} = f(x+1, y) + f(x-1, y) - 2f(x,y) 2x2f=f(x+1,y)+f(x1,y)2f(x,y)
(x,y)表示图像中的某一个像素值

同样的,在y方向的偏导就是:
∂ 2 f ∂ 2 y = f ( x , y + 1 ) + f ( x , y − 1 ) − 2 f ( x , y ) \frac{\partial^2 f}{\partial^2 y} = f(x, y+1) + f(x, y-1) - 2f(x,y) 2y2f=f(x,y+1)+f(x,y1)2f(x,y)

根据第一个公式,两者相加就是
∇ 2 f = ∂ 2 f ∂ 2 x = f ( x + 1 , y ) + f ( x − 1 , y ) + f ( x , y + 1 ) + f ( x , y − 1 ) − 4 f ( x , y ) \nabla^2f=\frac{\partial^2 f}{\partial^2 x} = f(x+1, y) + f(x-1, y) + f(x, y+1) + f(x, y-1) - 4f(x,y) 2f=2x2f=f(x+1,y)+f(x1,y)+f(x,y+1)+f(x,y1)4f(x,y)

从图像意义上来看,对某一个图像数据进行二阶求导就是等于这个像素的上下左右像素值减去自己像素值的四倍。
在图像中就可以使用一个掩膜来进行计算:
[ 0 1 0 1 − 4 1 0 1 0 ] \begin{bmatrix}0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0\end{bmatrix} 010141010

这个掩膜就是在图像领域的拉普拉斯算子。

计算图像的散度就可以直接将拉普拉斯算子作用于图像上进行计算。
我个人理解这个拉普拉斯可以提取图像中的边缘信息,因为边缘信息的梯度较小,如果一个像素点旁边的像素值都是相同的(也就是没有变化),这个算子计算得出的结果就是0。

融合图像散度

在那边论文描述的融合过程中,应该是把两幅图像分别求梯度场(一阶导数),然后把两幅图像的梯度场相加,然后再对融合梯度场进行一次求导,就是二次梯度场(散度),这就得到了融合图像的散度场。

通过散度场进行图像重建

对论文中的公式看的不是太明白,大概的意思是:
根据拉普拉斯算子的定义,图像进行拉普拉斯算子的卷积运算后就可以得到散度场。

那么,反过来,得到了散度场是否就可以得到原图像呢?答案是肯定的。

大致的思路如下:

  • 假设有这么一副图像:
    [ x 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 ] \begin{bmatrix} x_1 & x_2 & x_3 \\ x_4 & x_5 & x_6 \\ x_7 & x_8 & x_9\end{bmatrix} x1x4x7x2x5x8x3x6x9

  • 这幅图像对应的散度场: V ( 5 ) = x 2 + x 4 + x 6 + x 8 − 4 x 5 V(5) = x_2 + x_4 + x_6 + x_8 - 4x_5 V(5)=x2+x4+x6+x84x5

  • 总共有9个未知数,现在有了一个方程,如果还有8个方程的话,那么每个未知数就会有一个固定的解了。

  • 关键的点来了:泊松融合的目的是将源图像的一部分无缝融合到目标图像上。其本质是在保持目标图在融合边界的像素的同时以源图像的该部分的梯度场作为指导来生成融合区域内的像素。整体原则是保持融合区域内的生成像素的梯度场与源图像融合部分的像素的梯度场尽可能一致,反映到方程求解中则是梯度差异尽可能小。也就是让生成区域的拉普拉斯结果和源图像的拉普拉斯结果一致,且生成区域边界的的值和目标图像在融合区域的边界值一致。

  • 上面这句话最后说的边界值需要一致,也就是说融合图像的边界值可以和目标图像一致,也就是说上面的9个未知量(不管这个是融合的还是原图像)中,8个是边界,边界像素可以和目标图像一致,也就是已知量,加上这8个方程,联合散度场的计算公式,就可以重建出原始的图像了。

泊松融合的一般逻辑

综上所述,我理解泊松融合就是下面的逻辑:

  1. 计算需要被融合的图像的梯度场
  2. 通过掩码得到目标图像上的ROI区域,计算的到这个ROI的梯度场。
  3. 把第一步和第二步的梯度场相加
  4. 对第三步的梯度场再次求导,得到融合图像的二阶导,也就是散度场
  5. 再对这个散度场进行图像重建,得到融合图像。

在opencv中的seamlessClone函数中,有三个flag方式:

  • NORMAL_CLONE
  • MIXED_CLONE
  • MONOCHROME_TRANSFER

我还没去仔细看opencv的源码,根据我自己的实验,我个人感觉这三个参数主要在于上面的第三步的区别,可能是对两遍的梯度场做了一些权重的设置,看是以哪个梯度场为主来进行图像的生成。

  • NORMAL_CLONE,前需要融合的前景图像为主,边缘基本都保留了
  • MIXED_CLONE,两者的特性都保留
  • MONOCHROME_TRANSFER,保留前景的轮廓,把背景的风格给融合进去。上面没有这样的图像,这里补充一张:

在这里插入图片描述

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

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

相关文章

地理空间数据共享资源大汇总

1.全国12.5米分辨率ALOS高程数据 全国12.5米分辨率DEM数据,该数据由锐多宝的地理空间提供并进行分省裁剪与镶嵌,由测绘营地进行影像坐标系转换。 ALOS-12.5m高程数据参数: 覆盖范围:全国(仅有小部分区域存在数据空白…

本机使用python操作hdfs搭建及常见问题

一.虚拟机安装CentOS7并配置共享文件夹 二.CentOS 7 上hadoop伪分布式搭建全流程完整教程 三.本机使用python操作hdfs搭建及常见问题 四.mapreduce搭建 五.mapper-reducer编程搭建 本机使用python操作hdfs搭建及常见问题一、环境搭建1.打开虚拟机系统,打开hadoop2.修…

【JavaScript 逆向】极验四代滑块验证码逆向分析

前言 相较于三代滑块,四代的逻辑流程更简短,底图没混淆,某些点校验不严格 声明 本文章中所有内容仅供学习交流,相关链接做了脱敏处理,若有侵权,请联系我立即删除! 案例目标 滑动验证码&…

[NAS] QNAP/威联通 常用设置和操作

🍁简介 QNap 产品是一种可扩展的数据存储解决方案。它们包括具有 1 到 30 个驱动器托架的设备,并提供 HDMI、Thunderbolt 2 和 USB 3.1 等连接选项,以及 802.11ac/a/n Wi-Fi 和高达每秒 40 Gb 的以太网。内置软件提供基本服务,例如…

WeetCode2滑动窗口系列

一丶[无重复字符的最长子串](3. 无重复字符的最长子串 - 力扣(Leetcode))# 思路:# 维护一个窗口,窗口中不存在重复的字符,窗口右边界从第一个字符移动到最后,使用一个变量记录窗口大小的最大值 那么问题就变成了&…

浅谈h264和h265的区别

相比h264,压缩同样的视频获得同样的质量的情况下,h265可以做到压缩后的大小为前者的一半,但压缩时间复杂度增加。h264编码单元为宏块(MB),最大划分为16x16,而h265编码单元为编码树单元(CTU),最大划分为64x6…

C++ 不知算法系列之深入动态规划算法思想

1. 前言 前面写过一篇博文,介绍了什么是动态规划算法。动态规划算法的最大特点,原始问题可以通过分解成规模更小的子问题来解决,子问题之间互成依赖关系,先计算出来的子问题的结果会影响到后续子问题的结果。 有点类似于武侠片中…

Python——变量以及基础数据类型练习题

要求:注意变量名的命名规范问题!!!不能再出现没有意义的变量名!!!一行一注释,用下划线命名法。 请使用相对应的数据类型,不能全部使用字符串!!&a…

HotSpot VM垃圾收集器——Serial Parallel CMS G1垃圾收集器的JVM参数、使用说明、GC分析

目录HotspotVM的垃圾收集器简介1. Serial Collector2. Parallel Collector(throughput collector)3. Concurrent Mark Sweep Collector(CMS)4. Garbage-First Garbage Collector(G1)5. Z Garbage Collector…

STM32实战总结:HAL之GUI

在TFT上简单的显示字符、数字、汉字、图形、图片等,都是一些简单的显示。如果想要进行较为复杂的显示,就推荐使用GUI。 市面上常见的嵌入式GUI有LVGL,emWin(ucGUI),TouchGFX,Embedded GUI、QT f…

[附源码]计算机毕业设计springboot基于vuejs的爱宠用品销售app

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

ES系列二之常见问题解决

一 更新ES信息报错 报错信息如下:Use ElasticsearchException.getFailedDocuments() for detailed messages [{yjZ8D0oBElasticsearchException[Elasticsearch exception [typecluster_block_exception, reasonindex [au_report] blocked by: [FORBIDDEN/12/index …

c++ vector的模拟实现以及迭代器失效问题

目录 1.vector的模拟实现 2.迭代器失效问题 3.总结 1.vector的模拟实现 这里&#xff0c;我们使用三个指针来控制vector。 用_start指向头&#xff0c;_finish指向最后一个元素的尾巴&#xff0c;_end指向最大容量。 #include<iostream> #include<cassert>usin…

Spring Cloud Netfix Hystrix(断路器)

一、灾难性雪崩 造成灾难性雪崩效应的原因&#xff0c;可以简单归结为下述三种&#xff1a; 服务提供者&#xff08;Application Service&#xff09;不可用。如&#xff1a;硬件故障、程序BUG、缓存击穿、并发请求量过大等。 重试加大流量。如&#xff1a;用户重试、代码重试…

手记:把代码上传到Gitee等远程仓库的过程记录及常见问题

很久没用git了&#xff0c;指令都有点生疏了&#xff0c;今天上传了一些代码到码云上&#xff0c;先把过程记录下来供使用git的朋友参考。没有用图形化界面&#xff0c;因为只有熟悉指令才能真正的理解领会。 步骤一&#xff1a; 1、安装git&#xff1b;安装后可以使用指令git…

打造一个投资组合管理的金融强化学习环境

原创文章第120篇&#xff0c;专注“个人成长与财富自由、世界运作的逻辑&#xff0c; AI量化投资”。 今天继续金融强化学习环境。 网上的金融学习环境不少&#xff0c;但都太过于“业余”&#xff0c;或者离像样的投资还差得太远。我一直觉得投资组合应该是必要的&#xff0…

怎么恢复已删除的全部数据,不小心删除的数据怎么恢复,删除的文件还能找回吗

怎么恢复已删除的全部数据&#xff1f;一般来讲&#xff0c;当文件被删除后&#xff0c;都会暂时被放置在回收站的位置&#xff0c;如果我们想找回相应的丢失数据&#xff0c;具体该如何操作呢&#xff1f; 一、当回收站没有被清空 这是最简单的一种恢复误删数据的方法&#…

前端入门--JavaScript篇

JavaScript基础 文章目录JavaScript基础JavaScript是什么JavaScript的使用方式JavaScript的运行过程JS的语法三种语言的注释输入输出JS中的变量JS中基本的数据类型number类型string字符串undefined类型null类型运算符数组数组的创建数组的使用数组新增元素函数对象之前学过了HT…

缓存的设计

文章目录1. 缓存的更新机制1.1 被动更新1.2 主动更新1.2.1 Cache Aside Pattern &#xff08;更新数据库&#xff0c;再删除缓存&#xff09;1.2.2 更新数据库&#xff0c;更新缓存1.2.3 先删除缓存&#xff0c;在更新数据库1.3 Read/Write Through Pattern1.4 Write Behind Ca…

【Linux】权限管理

文章目录一、shell 命令以及运行原理二、Linux的用户权限1、权限概念引入2、用户分类3、用户切换4、用户提权三、Linux 权限管理1、文件访问者的角色划分2、文件类型和访问权限a、文件类型(后缀理解 file 指令讲解)b、文件访问权限四、文件访问权限的更改1、chmod 指令(对拥有…