Python 语言学习——应用1.2 数字图像处理(第二节,变换)

目录

1.基础知识

        1.图像几何变换概念

        2.图像几何变换方式 

        3.插值运算

        4.几何变换步骤 

2.各类变换 

        1.位置变换

         2.形状变换

        3.代数运算 

3.实战演练 


1.基础知识

        1.图像几何变换概念

  • 在图像处理过程中,为了观测需要,常常需要对 图像进行几何变换,如几何失真图像的校正、图 像配准、电影、电视和媒体广告等的影像特技处 理等,是图像变形以及校正变形的基础。
  • 图像几何变换将图像中任一像素映射到一个新位置,是一种空间变换,关键在于确定图像中点与点之间的映射关系

        2.图像几何变换方式 

  • 首先有齐次坐标:用n+1维向量表示n维向量的方法称为齐次坐标 表示法。原图像用点集[x,y,1]^{T}(转置的意思)表示。好处:齐次坐标中,对原图像进行平移、缩放、旋转等 几何变换,可用一个变换矩阵表示。
  • 然后是变换矩阵:a,b,c,d用于图形的比例、对称、 错切、旋转等基本变换;k,m用于 图形的平移变换;p,q用于投影变 换;s用于全比例变换。
  • 实现2D图像几何变换的基本变换的一般过程是:变换矩阵T×变换前的点集矩阵=变换后的点集矩阵。 

        3.插值运算

  • 为什么会有这玩意:当你调整图像的大小,特别是缩小图像时,由于目标大小的像素数量少于原图像的像素数量,就需要通过插值算法来估算新像素的值。在进行旋转、平移、拉伸等几何变换时,图像的像素位置可能会发生变化(比如原x,y的像素点变成0.8x,0.9y算出来是个小数,则相应位置像素值不知道是多少)应用一些滤波器或处理器时,如模糊、锐化等操作,会导致像素值的变化,需要通过插值来重新计算像素值。

  • 概念:指利用已知邻近像素点的灰度值来产生未知像素点的灰度值(各通道)

  • 常用插值运算方法:

  1. 最近邻插值:非整数像素灰度值就等于距离最近 的坐标都为整数的像素的灰度值。 
  2. 双线性插值:利用非整数像素点周围的四个像素 点的相关性,通过双线性算法计算得出。图1算法或图2算法(利用周边四个点)。

  3. 双三次插值:利用非整数像素点周围的16个像素 点进行计算。

        4.几何变换步骤 

  1. 根据不同的几何变换公式计算新图像的尺寸
  2. 根据几何变换的逆变换,对新图像中的每一点确 定其在原图像中的对应点
  3. 按对应关系给新图像中各个像素赋值
  • 若新图像中像素点在原图像中的对应点坐标存 在,直接赋值
  • 若新图像中像素点在原图像中的对应点坐标超 出图像宽高范围,直接赋背景色
  • 若新图像中像素点在原图像中的对应点坐标在 图像宽高范围内,采用插值的方法计算 

2.各类变换 

        1.位置变换

  • 图像的位置变换是指图像的大小和形状不发生变化,只是图像像素点的位置发生变化,含平 移、镜像、旋转
  • 平移:若不想丢失信息,可能需要扩大画布

  • 镜像:M为总行数,N为总列数 

  • 旋转: θ为正,代表逆时针旋转,若要确定变换后的尺寸大小:计算原图像四个角在 旋转后的坐标;确定新图像的分辨率,M'=max x'-min x'+1;N'=max y'-min y'+1(结果向上取整).对于不在原点旋转的,先要将坐标系平移到原点,再按绕原点旋转进行变换,然后平移回原坐标原点。得到新图像的M',N'后,需依次确定[0,M'-1]+minx',[0,N'-1]+miny'各个像素点的值。也就是再逆变换回去,根据步骤第三点的三条进行赋值。

         2.形状变换

  • 缩放

  • 错切:平面景物在投影平面上的非垂直投 影,使图像中的图形产生扭变

     

        3.代数运算 

  • 加法运算如下图,应用主要有:多幅图像相加求平均去除叠加性噪声,将一幅图像的内容经配准后叠加到另一幅图像 上去,以改善图像的视觉效果,在多光谱图像中,通过加法运算加宽波段,如 绿色波段和红色波段图像相加可以得到近似全色图像,用于图像合成和图像拼接。

  • 减法运算如下图,应用主要有:显示两幅图像的差异,检测同一场景两幅图像 之间的变化;去除不需要的叠加性图案,加性图案可能是缓 慢变化的背景阴影或周期性的噪声,或在图像 上每一个像素处均已知的附加污染等;图像分割:如分割运动的车辆,减法去掉静止 部分,剩余的是运动元素和噪声;生成合成图像。 

  • 乘法运算主要是部分图框出来,也就是图像的局部显示和提取:用二值模板图像与 原图像做乘法来实现 。

  • 逻辑运算:原理如图。


3.实战演练 

P1. 试编写程序,对图像逆时针旋转60°,采用双线性插值的方法

from PIL import Image  # 导入PIL库,用于图像处理
import math #用于旋转时计算三角函数def bilinear_interpolation(image, x, y):# 将坐标转换为整数x1, y1 = int(x), int(y)x2, y2 = x1 + 1, y1 + 1# 处理边界情况if x2 >= image.width:x2 = x1if y2 >= image.height:y2 = y1# 获取四个相邻像素的RGB值q11 = image.getpixel((x1, y1))q21 = image.getpixel((x2, y1))q12 = image.getpixel((x1, y2))q22 = image.getpixel((x2, y2))r = []#空列表的创建# 处理 RGB 值的每个通道for i in range(3):  # 0: 红色通道, 1: 绿色通道, 2: 蓝色通道r.append(q11[i] * (x2 - x) * (y2 - y) + q21[i] * (x - x1) * (y2 - y) + q12[i] * (x2 - x) * (y - y1) + q22[i] * (x - x1) * (y - y1))return tuple(map(int, r))  # 返回处理后的 RGB 值def rotate_image(image, angle):# 旋转图像函数angle = angle % 360if angle == 0:return imagerotated_image = Image.new("RGB", image.size)  # 创建旋转后的图像对象for x in range(rotated_image.width):for y in range(rotated_image.height):# 计算旋转后的坐标,即逆变换找到原坐标位置x_original = ((x - rotated_image.width / 2) * math.cos(math.radians(angle)) -(y - rotated_image.height / 2) * math.sin(math.radians(angle)) + image.width / 2)y_original = ((x - rotated_image.width / 2) * math.sin(math.radians(angle)) +(y - rotated_image.height / 2) * math.cos(math.radians(angle)) + image.height / 2)if 0 <= x_original < image.width - 1 and 0 <= y_original < image.height - 1:# 应用双线性插值rotated_image.putpixel((x, y), bilinear_interpolation(image, x_original, y_original))#注意写法,这个函数第二个参数是元组表示的一组rgb的值return rotated_image# 打开图像文件
image = Image.open("rainbow.jpg")# 逆时针旋转60°
rotated_image = rotate_image(image, 60)# 保存旋转后的图像
rotated_image.save("rotated_image.jpg")  # 保存旋转后的图像

说明:map(int, r): 这部分使用了Python内置的 map 函数,它接受一个函数和一个可迭代对象作为参数。在这里,int 函数被应用于可迭代对象 r 中的每个元素,将每个元素转换为整数。tuple(...): tuple() 函数用于将一个可迭代对象转换为元组(tuple)。在这里,map(int, r) 返回一个迭代器,然后 tuple() 将这个迭代器转换为一个由整数组成的元组。

P2. 打开一幅图像,依次完成下列要求:顺时针旋转20°,做水平镜像,做错切变换,缩小图像。若需要插值运算,采用双线性插值方法;要求输出显示原图、中间结果和最后结果。(作为模板题)

import math #用于旋转时计算三角函数
from PIL import Imagedef resize_image(image, width_scale, height_scale):# 获取图像的宽度和高度width, height = image.size# 计算放大或缩小后的宽度和高度new_width = int(width * width_scale)new_height = int(height * height_scale)# 创建一个新图像对象,用于存储放大或缩小后的图像resized_image = image.resize((new_width, new_height))return resized_imagedef shear_image(image, shear_factor):# 获取图像的宽度和高度width, height = image.size# 计算错切后的宽度,只需计算宽度即可new_width = width + int(abs(shear_factor) * height)# 创建一个新图像对象,用于存储错切后的图像sheared_image = Image.new("RGB", (new_width, height))# 错切处理for x in range(new_width):for y in range(height):# 计算错切后的坐标,逆旋转new_x = x - int(shear_factor * y)if 0 <= new_x < width:# 获取原始图像中的像素值pixel = image.getpixel((new_x, y))# 将像素值复制到错切图像中sheared_image.putpixel((x, y), pixel)return sheared_imagedef mirror_image(image):# 获取图像的宽度和高度width, height = image.size# 创建一个新图像对象,用于存储镜像后的图像mirrored_image = Image.new("RGB", (width, height))# 镜像处理for x in range(width):for y in range(height):# 获取原始图像中的像素值pixel = image.getpixel((x, y))# 将像素值复制到镜像图像中,但是在水平方向上镜像mirrored_image.putpixel((width - x - 1, y), pixel)return mirrored_imagedef bilinear_interpolation(image, x, y):# 将坐标转换为整数x1, y1 = int(x), int(y)x2, y2 = x1 + 1, y1 + 1# 处理边界情况if x2 >= image.width:x2 = x1if y2 >= image.height:y2 = y1# 获取四个相邻像素的RGB值q11 = image.getpixel((x1, y1))q21 = image.getpixel((x2, y1))q12 = image.getpixel((x1, y2))q22 = image.getpixel((x2, y2))r = []#空列表的创建# 处理 RGB 值的每个通道for i in range(3):  # 0: 红色通道, 1: 绿色通道, 2: 蓝色通道r.append(q11[i] * (x2 - x) * (y2 - y) + q21[i] * (x - x1) * (y2 - y) + q12[i] * (x2 - x) * (y - y1) + q22[i] * (x - x1) * (y - y1))return tuple(map(int, r))  # 返回处理后的 RGB 值def rotate_image(image, angle):# 旋转图像函数angle = angle % 360if angle == 0:return imagerotated_image = Image.new("RGB", image.size)  # 创建旋转后的图像对象for x in range(rotated_image.width):for y in range(rotated_image.height):# 计算旋转后的坐标,即逆变换找到原坐标位置x_original = ((x - rotated_image.width / 2) * math.cos(math.radians(angle)) -(y - rotated_image.height / 2) * math.sin(math.radians(angle)) + image.width / 2)y_original = ((x - rotated_image.width / 2) * math.sin(math.radians(angle)) +(y - rotated_image.height / 2) * math.cos(math.radians(angle)) + image.height / 2)if 0 <= x_original < image.width - 1 and 0 <= y_original < image.height - 1:# 应用双线性插值rotated_image.putpixel((x, y), bilinear_interpolation(image, x_original, y_original))#注意写法,这个函数第二个参数是元组表示的一组rgb的值return rotated_image# 打开图像文件
image = Image.open("rainbow.jpg")
image.show()# 逆时针旋转-20°
rotated_image = rotate_image(image, -20)
rotated_image.show()mirror=mirror_image(rotated_image)
mirror.show()share=shear_image(mirror,2)
share.show()resize=resize_image(share,0.2,0.2)
resize.show()

P3. 打开两幅图像,利用几何变换、图像代数运算,生成一幅精美的合成图像

from PIL import Imagedef resize_image(image, width_scale, height_scale):# 获取图像的宽度和高度width, height = image.size# 计算放大或缩小后的宽度和高度new_width = int(width * width_scale)new_height = int(height * height_scale)# 创建一个新图像对象,用于存储放大或缩小后的图像resized_image = image.resize((new_width, new_height))return resized_imagedef add_images(sun_image, sky_image, output_path):# 获取太阳图像和天空图像的宽度和高度sun_width, sun_height = sun_image.sizesky_width, sky_height = sky_image.size# 确保太阳图像完全在天空图像内if sun_width <= sky_width and sun_height <= sky_height:# 创建一个新的图像对象,用于存储混合后的图像result_image = Image.new("RGB", (sky_width, sky_height))# 将天空图像复制到结果图像中result_image.paste(sky_image, (0, 0))# 将太阳图像叠加到左上角result_image.paste(sun_image, (0, 0), sun_image)# 保存混合后的图像result_image.show()else:print("Sun image is too large to fit entirely on the sky image.")# 图像文件路径sun_path = "sun.png"
sun_image = Image.open(sun_path)
sun_image.show()
sun=resize_image(sun_image,0.2,0.2)
sky_path = "sky.jpg"
output_path = "result_image.jpg"
sky_image = Image.open(sky_path)
sky_image.show()
# 执行图像混合操作
add_images(sun, sky_image, output_path)

 说明,paste函数的使用:语法为image.paste(image_to_paste, box, mask)image_to_paste: 要粘贴的图像对象。box: 表示将图像粘贴到另一个图像的位置,通常是一个元组 (x, y),表示左上角的坐标。mask: 用于指定一个掩码图像,一般而言为数值,从0不透明到255透明,也可以是一个二值图像,如果提供了掩码图像,它将根据掩码的像素值来控制被粘贴图像的透明度,使得被粘贴图像可以部分透明地展现在目标图像上。

题外话:不要吐槽图像的精美程度。

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

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

相关文章

Kali或Debian系统安装JDK1.8保姆级教程

一、下载JDK1.8 先到Oracle的官网下载JDK1.8 Java Archive | Oraclehttps://www.oracle.com/java/technologies/downloads/archive/Java Archive Downloads - Java SE 8

【springboot】整合沙箱支付

目录 1. 配置沙箱应用环境2. 配置springboot项目1. 引入依赖2. 配置文件注册下载ngrok 3. 创建支付宝支付服务类4. 支付界面模板5. 控制类实现支付6. 测试 1. 配置沙箱应用环境 使用支付宝账号登录到开放平台控制台。 使用支付宝登录后&#xff0c;看到以下页面&#xff0c;下…

云计算身份认证与访问控制(Cloud Computing Identity Authentication and Access Control)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

AHT10原理讲解(I2C驱动)-基于江科大源码开发

一、原理图 根据数据手册中的要求&#xff08;以上两图&#xff09;&#xff0c;可以看出SDA和SCL接主芯片的引脚就可以&#xff0c;但是注意我们在接的过程中&#xff0c;给了两个上拉电阻。 上拉电阻的作用&#xff1a; 提高抗干扰能力&#xff1a;适当的上拉电阻值可以帮助…

论文阅读笔记-LogME: Practical Assessment of Pre-trained Models for Transfer Learning

前言 在NLP领域,预训练模型(准确的说应该是预训练语言模型)似乎已经成为各大任务必备的模块了,经常有看到文章称后BERT时代或后XXX时代,分析对比了许多主流模型的优缺点,这些相对而言有些停留在理论层面,可是有时候对于手上正在解决的任务,要用到预训练语言模型时,面…

数据库三大范式

第一范式&#xff08;1NF&#xff09; 确保每个列中的数据是不可再分的。即&#xff0c;每个列只能包含一个值&#xff0c;而不是一个列表或数组。 每个列有多个数据的要拆成多个表。 错误&#xff1a; 正确&#xff1a; 第二范式 &#xff08;2NF&#xff09; 在第一范式的基…

探索Spring Boot:实现“衣依”服装电商平台

1系统概述 1.1 研究背景 如今互联网高速发展&#xff0c;网络遍布全球&#xff0c;通过互联网发布的消息能快而方便的传播到世界每个角落&#xff0c;并且互联网上能传播的信息也很广&#xff0c;比如文字、图片、声音、视频等。从而&#xff0c;这种种好处使得互联网成了信息传…

数据结构与算法——Java实现 30.合并多个有序链表 小顶堆实现

后来我们都走了很久&#xff0c;远到提及往事时&#xff0c; 总会加上once upon a time —— 24.10.6 23. 合并 K 个升序链表 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 示例 1&#xff1…

【EXCEL数据处理】000010 案列 EXCEL文本型和常规型转换。使用的软件是微软的Excel操作的。处理数据的目的是让数据更直观的显示出来,方便查看。

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【EXCEL数据处理】000010 案列 EXCEL单元格格式。EXCEL文本型和常规型转…

推荐一个可以把PDF样本册转换为翻页电子书的网站

​随着互联网的普及&#xff0c;越来越多的企业和个人开始意识到线上展览的重要性。如何将实体样本册转化为线上版本&#xff0c;让更多人了解和欣赏自己的产品与服务&#xff1f; 一、网站简介 这款PDF样本册免费上传网站名为“FLBOOK”&#xff0c;致力于为广大用户提供便捷…

构建 10 万卡 GPU 集群的技术挑战

构建 10 万卡 GPU 集群的技术挑战 摘要 揭示AI训练集群关键基础设施挑战&#xff0c;探讨突破现有AI瓶颈的必要性与10万GPU集群&#xff08;如OpenAI、Meta&#xff09;建设所面临挑战与需求。 构建网络拓扑&#xff0c;需权衡多层交换机成本、带宽与维护。本文对比Ethernet与…

【IEEE PDF eXpress】格式不对

目录 一、问题二、解决方法 一、问题 word的文档&#xff0c;用IEEE PDF eXpress网站生成pdf后&#xff0c;提交论文出现错误&#xff1a; Document validation failed due to the following errors: Content exceeds IEEE template margins for its format (Page 1:Bottom).…

螺蛳壳里做道场:老破机搭建的私人数据中心---Centos下Docker学习04(环境准备)

4 创建docker容器 4.1创建网络 [rootlocalhost wutool]# docker network create -d macvlan --subnet192.168.137.0/24 --gateway192.168.137.2 --ip-range192.168.137.0/24 -o parentens33 nat 52af11381bfd655d175e4168265b2a507793e8fe48f119db846949ffd4dd27de [rootlocal…

【JavaWeb】javaweb目录结构简介【转】

以上图说明&#xff1a; bbs目录代表一个web应用bbs目录下的html,jsp文件可以直接被浏览器访问WEB-INF目录下的资源是不能直接被浏览器访问的web.xml文件是web程序的主要配置文件所有的classes文件都放在classes目录下jar文件放在lib目录下

Stream流的终结方法(二)——collect

1.Stream流的终结方法 2. collect方法 collect方法用于收集流中的数据放到集合中去&#xff0c;可以将流中的数据放到List&#xff0c;Set&#xff0c;Map集合中 2.1 将流中的数据收集到List集合中 package com.njau.d10_my_stream;import java.util.*; import java.util.f…

鸿蒙开发(NEXT/API 12)【管理应用与Wear Engine服务的连接状态】手机侧应用开发

监测应用与Wear Engine服务的连接状态 华为运动健康App在后台停止服务&#xff08;如功耗过高&#xff09;&#xff0c;从而导致应用与Wear Engine服务的连接状态发生变化。对于类似这种不确定的断开情况&#xff0c;开发者可以通过本功能特性了解当前应用和Wear Engine的连接…

五子棋双人对战项目(3)——匹配模块

目录 一、分析需求 二、约定前后端交互接口 匹配请求&#xff1a; 匹配响应&#xff1a; 三、实现游戏大厅页面&#xff08;前端代码&#xff09; game_hall.html&#xff1a; common.css&#xff1a; game_hall.css&#xff1a; 四、实现后端代码 WebSocketConfig …

初识算法 · 双指针(2)

目录 前言&#xff1a; 盛最多水的容器 题目解析&#xff1a; 算法原理&#xff1a; 算法编写&#xff1a; 有效三角形的个数 题目解析&#xff1a; 算法原理&#xff1a; 算法编写&#xff1a; 前言&#xff1a; 本文介绍两个题目&#xff0c;盛最多水的容器和有效三…

【Blender Python】5.Blender场景中的集合

概述 这里的“集合”是指Blender场景中的集合。你可以在“大纲视图”面板中看到 图标的&#xff0c;就是集合&#xff0c;可以看做是文件夹&#xff0c;用于分类和整理场景中的对象。 获取场景的集合 >>> C.scene bpy.data.scenes[Scene]>>> C.scene.coll…