当前位置: 首页 > news >正文

2.1 OpenCV随手简记(二)

为后续项目学习做准备,我们需要了解LinuxOpenCV、Mediapipe、ROS、QT等知识。

一、图像显示与保存

1、基本原理

1.1 图像像素存储形式

首先得了解下图像在计算机中存储形式:(为了方便画图,每列像素值都写一样了)。对于只有黑白颜色的灰度图,为单通道,一个像素块对应矩阵中一个数字,数值为0到255, 其中0表示最暗(黑色) ,255表示最亮(白色)

对于采用RGB模式的彩色图片,为三通道图,Red、Green、Blue三原色,按不同比例相加,一个像素块对应矩阵中的一个向量, 如[24,180, 50],分别表示三种颜色的比列, 即对应深度上的数字,如下图所示:

 需要注意的是,由于历史遗留问题,opencv采用BGR模式,而不是RGB


2、API说明

API名: cv2.destroyAllWindows( )

删除所有我们建立的窗口

API名: cv2.destroyWindow(指定窗口名)

删除特定的窗口可以使用,在括号内输入你想删 除的窗口名。

API名: cv2.imshow('窗口名', 图像变量):

用“窗口名”显示图像

API名:cv2.resizeWindow(‘窗口名’, 宽, 高)  

调整窗口大小

API名:cv2.imwrite('图像文件地址',图像变量)

将图像变量保存到指定位置的指定文件中

 API名:cv.namedWindow(winname, flags=None)

参数:winname

窗口名字

flags窗口标志

标志参数

作用

WINDOW_NORMAL

显示图像后,允许用户随意调整窗口大小

WINDOW_AUTOSIZE

根据图像大小显示窗口,不允许用户调整大小

WINDOW_FREERATIO

窗口大小自适应比例

WINDOW_KEEPRATIO

保持图像的比例

1、具体代码展示标志效果

1.1 窗口大小可变

cv.namedWindow("show Image",cv.)

或者 cv.namedWindow("show Image",cv.WINDOW_GUI_NORMAL)

或者 cv.namedWindow("show Image",0)

此时的图片是可以自由拉伸改变大小的

1.2 窗口大小不可变,自动适应图片大小(默认)

cv.namedWindow("show Image",cv.WINDOW_AUTOSIZE)

或者 cv.namedWindow("show Image",1)

1.3 窗口大小自适应比例

cv.namedWindow("show Image",cv.WINDOW_FREERATIO)

1.4 窗口大小跟随图片保持其比例

cv.namedWindow("show Image",cv.WINDOW_KEEPRATIO)

说明:如果在imshow()之前加上namedWindow()方法来显示一张图片的话,该窗口显示的图片是可交互的。

1、这里使用imshow()和namedWindow()方法时候窗口的标识名称(传递的第一个参数)要一样。

2、namedWindow()方法要写在imshow()方法之前才可以。

 API名:cv.namedWindow(winname, flags=None)

参数:winname

窗口名字

flags窗口标志

标志参数

作用

WINDOW_NORMAL

显示图像后,允许用户随意调整窗口大小

WINDOW_AUTOSIZE

根据图像大小显示窗口,不允许用户调整大小

WINDOW_FREERATIO

窗口大小自适应比例

WINDOW_KEEPRATIO

保持图像的比例

1、具体代码展示标志效果

1.1 窗口大小可变

cv.namedWindow("show Image",cv.)

或者 cv.namedWindow("show Image",cv.WINDOW_GUI_NORMAL)

或者 cv.namedWindow("show Image",0)

此时的图片是可以自由拉伸改变大小的

1.2 窗口大小不可变,自动适应图片大小(默认)

cv.namedWindow("show Image",cv.WINDOW_AUTOSIZE)

或者 cv.namedWindow("show Image",1)

1.3 窗口大小自适应比例

cv.namedWindow("show Image",cv.WINDOW_FREERATIO)

1.4 窗口大小跟随图片保持其比例

cv.namedWindow("show Image",cv.WINDOW_KEEPRATIO)

说明:如果在imshow()之前加上namedWindow()方法来显示一张图片的话,该窗口显示的图片是可交互的。

1、这里使用imshow()和namedWindow()方法时候窗口的标识名称(传递的第一个参数)要一样。

2、namedWindow()方法要写在imshow()方法之前才可以。


三、代码示例

使用 Matplotlib显示图片

import cv2

from matplotlib import pyplot as plt

 

if __name__ == "__main__":

    img = cv2.imread('1.jpg',0)

    plt.imshow(img, cmap = 'gray', interpolation = 'bicubic')

    plt.xticks([]), plt.yticks([])  # to hide tick values on X and Y axis

plt.show()

使用 Matplotlib显示图片并进行一些操作

import cv2

from matplotlib import pyplot as plt

 

if __name__ == "__main__":

    img = cv2.imread('2.jpg')

    edges = cv2.Canny(img,100,200)

 

    plt.subplot(121),plt.imshow(img,cmap = 'gray')

    plt.title('Original Image'),  plt.xticks([]), plt.yticks([])

    plt.subplot(122),  plt.imshow(edges,cmap = 'gray')

    plt.title('Edge Image'), plt.xticks([]), plt.yticks([])

    plt.show()

说明:plt.imshow()函数负责对图像进行处理,并显示其格式,而plt.show()则是将plt.imshow()处理后的函数显示出来。



二、图像处理基础

1.1、获取图像属性

所谓获取图像属性,实际上就说当我们把一幅图像读入内存后,这幅图像就如同一个三维或二维数组,那既然是个numpy数组,必然可以获取它的shape、它的size以及它的dtype。乃至于,你可以像操作numpy数组那样,在图像上做诸如求平均值、方差之类的各种数学操作。因此,实际上获取图像的属性操作也很简单,比如下面这个例子就分别展示了如何获得图像的shape(指示是三维还是二维图像,或者说彩色图还是灰度图)、size(图像中像素的总数,依据这个总数以及每个像素的dtype就可以获得这幅图像当前总共占据多少内存存储空间)、dtype(每个像素值的数据类型:float32还是int等等):

img = cv2.imread('test.jpg')

获取图像的属性

img.shape

可以获取图像的形状。他的返回值是:一个包含(行数(高),列数(宽), 通道数)的元组。

img.size

返回图像的像素数目

img.dtype

返回返回图像的数据类型

API名:cv2.imwrite('图像文件地址',图像变量)

将图像变量保存到指定位置的指定文件中

先查看原始图像的shape、size、dtype:(675, 1200, 3) 2430000 uint8

再查看颜色模式为HSV后的图像的shape、size、dtype:(675, 1200, 3) 2430000 uint8

最后查看变成灰度图后图像的shape、size、dtype:(675, 1200) 810000 uint8

从结果来看,当彩色图变成灰度图之后,通道数没有了,意味着图像的像素总数也会大幅减少。


1.2 图像像素更改

        图像像素的更改行为,往往在机器视觉中是最最简单的一种行为,其操作起来也如同我们更改一个list或者一个numpy数组中的元素那样简单。要知道,任何一幅图像实际上读到内存之后就是一个numpy数组,因此,图像像素的修改就如同我们在修改一个n维数组中的某个元素那样简单。所不同的是,修改图像像素,我们需要指定图像像素的坐标:

         如果是RGB格式的,坐标就分别是R、G、B三个通道的值来共同定位一个像素;如果是HSV或别的什么颜色模式下的图像,也如同像RGB那样指定出三维坐标系各个坐标轴上的坐标值即可。

        如果是一个灰度图,则如同一个平面坐标系那般简单,只需要指定横纵坐标的值即可圈定一个像素。

        当然,通常情况下,我们只需要给出像素的行和列坐标值即可获得像素内的值。此时,对于RGB或HSV等三维颜色格式的图像来说,返回的是图像的三维坐标的值,比如RGB就是返回R、G、B;HSV的就是返回H、S、V;对于灰度图来说则是返回的是灰度值。

        同时,给定像素的行、列坐标值就可以依据像操作numpy二维数组那样来给指定的像素点修改像素值。

获取像素值并修改

参数含义:y坐标x:x坐标

bgr:BGR通道,0:B通道,1:G通道,2:R通道

获取像素值 img[y,x]

获取(x,y)处的通道值,返回列表


img = cv2.imread('test.jpg')

# 获取像素值px = img[100, 200]

img[y,x,bgr] 

获取(x,y)处一个通道值

获取像素中的B通道颜色值blue = img[100, 200, 0]

修改像素值 img[y,x]=[b,g,r]

将一个颜色赋值给一个像素点

# 修改像素值img[100, 200] = [0, 123, 155]

比如下面这段小代码就很好地解释了如何获取不同颜色格式下的像素值内容:

import numpy as npimport cv2#读入原始图片,并将其进行颜色空间转换为HSV和灰度图#再分别读出相同的行、列坐标值下的像素值以加深对像素值更操作的理解
import numpy as npimport cv2#读入原始图片,并将其进行颜色空间转换为HSV和灰度图#再分别读出相同的行、列坐标值下的像素值以加深对像素值更操作的理解
img = cv2.imread(r'E:\tmp\maerdaifu.jpg',cv2.IMREAD_UNCHANGED)
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print('以下为不同颜色模式下获取像素值的输出结果')
print(img[100,100])
print(hsv[100,100])
print(gray[100,100])img[100,100]=[192, 212, 222]
hsv[100,100]=[198, 218, 228]
gray[100,100]=255print('以下为不同颜色模式下修改像素值的输出结果')
print(img[100,100])
print(hsv[100,100])
print(gray[100,100])

以下为不同颜色模式下获取像素值的输出结果

[217 121  55][108 190 217]  

以下为不同颜色模式下修改像素值的输出结果[192 212 222][198 218 228]

1.3 获取图像的感兴趣区域(ROI)

        图像的感兴趣区域(ROI:region of interest),是指在机器视觉、图像处理中,从被处理的图像以方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域,称为感兴趣区域。在图像处理领域,感兴趣区域是从图像中选择的一个图像区域,这个区域是你的图像分析所关注的重点。圈定该区域以便进行进一步处理。使用ROI圈定你想读的目标,可以减少处理时间,增加精度。感兴趣区是图像的一部分,它通过在图像上选择或使用诸如设定阈值(thresholding) 或者从其他文件(如矢量> 转换获得等方法生成。感趣区可以是点、线、面不规则的形状,通常用来作为图像分类的样本、掩膜、裁剪区或及其他操作。

        比如,我想要抠出下面这幅图中的房子:

图1 马尔代夫风景图

        那么首先我需要知道图中的房子大约在哪些像素坐标范围之内,也即其行坐标范围和列坐标范围。这里我们需要事先弄清楚感兴趣区域在图像中的坐标范围:即行列坐标的坐标值范围。

        确定坐标范围后,我们利用切片操作,像在Numpy数组中获取一定范围内的数据那样,即可获得我们感兴趣的区域内的目标对象——房子。整个获取ROI的代码如下所示:

import cv2
#读入原始图片
img = cv2.imread(r'E:\tmp\maerdaifu.jpg',cv2.IMREAD_UNCHANGED)
#扣除我们感兴趣的房子区域并予以单独显示
house = img[126:353,316:908]
cv2.imshow('HouseTest1', house)
#我们再把抠出来的房子放至原图像的其他区域,再显示操作过后的原始图像img[326:553,500:1092]=house
cv2.imshow('HouseTest2', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

其运行结果如下:

图2 抠出的ROI图像

图3 将ROI图像重新放置回原始图中的运行效果图

        从以上两幅图来看,我们大体上知道了如何来获取一个ROI图像。

        需要注意的是:图像在内存中的存储格式是一个numpy数组,因此,当你给定行列坐标值范围时,一定要把列坐标范围放置在切片操作的第一个参数值上,行坐标值范围为切片操作的第二个参数。这是大家在实际利用numpy数组开展切片操作或其他需要用到行列坐标参与运算时最最容易犯的错误之一。

1.4 图像通道的拆分、合并操作

        当我们需要单独对图像的通道进行操作,比如R、G、B三个通道都有不同的操作时,我们就需要用到通道的拆分。当拆分后的通道被处理结束之后,可能需要将其复原时,又需要用到通道的合并操作。由于我们再三强调过读入一幅图像到内存后,其实质就是读入一个numpy数组,因此,在numpy的基础知识讲解中,如何拆分和合并不同的维度,这里就可以用同样的API来操作,也即利用split()来拆分通道,利用merge()来合并通道,

拆分通道1: b,g,r = cv2.split(m,mv)

m:  图像imgmv:  默认为None

# 分离颜色通道

b, g, r = cv2.split(img)

cv2.imshow('Blue', b)

cv2.imshow('Green', g)

cv2.imshow('Red', r)

拆分通道2:

b=img[:,:,bgr]

# 其他方法

b1 = img[:, :, 0] # 仅取B通道

cv2.imshow('Blue-1', b1)

b2 = cv2.imread('test.jpg')

b2[:, :, 1:3] = 0 # 将0赋值给G、R通道

cv2.imshow('Blue-2', b2)

合并通道 img = cv2.merge([X,Y,Z])

mergeImg = cv2.merge([b, g, r])

cv2.imshow('merge', mergeImg )

mergeImg2 = cv2.merge([h, s, v])

cv2.imshow('merge', mergeImg2 )

import cv2#读入原始图片
img = cv2.imread(r'E:\tmp\maerdaifu.jpg',cv2.IMREAD_UNCHANGED)
r,g,b = cv2.split(img) #请大家注意一下merge函数的参数给定形式,如果不以list  或tuple方式来给定的话,必然会出错
img = cv2.merge([r,g,b])
cv2.imshow('Test', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

其运行结果如下:

图4 通道拆分合并效果图

可以看出,通道经拆分再合并后得到的图像与原始图像并无二致。

#通道的分离与合并以及某个通道值的修改
import cv2 as cv
src=cv.imread('E:\imageload\example.png')
cv.namedWindow('first_image', cv.WINDOW_AUTOSIZE)
cv.imshow('first_image', src)
#三通道分离形成单通道图片
b, g, r =cv.split(src)
cv.imshow("second_blue", b)
cv.imshow("second_green", g)
cv.imshow("second_red", r)
# 其中cv.imshow("second_red", r)可表示为r = cv2.split(src)[2]
#三个单通道合成一个三通道图片
src = cv.merge([b, g, r])
cv.imshow('changed_image', src)#修改多通道里的某个通道的值
src[:, :, 2] = 0
cv.imshow('modify_image', src)cv.waitKey(0)
cv.destroyAllWindows()

5、添加边界(padding)/为图像扩边

cv2.copyMakeBorder(src,top, bottom, left, right,borderType,value)

src: 输入图像

• top, bottom, left, right 对应边界的像素数目。

value: 边界颜色,如果borderType为cv2.BORDER_CONSTANT时,传入的边界颜色值,如[0,255,0]

borderType: 要添加那种类型的边界,类型如下

–cv2.BORDER_CONSTANT 添加有颜色的常数值边界,还需要下一个参数(value)。

– cv2.BORDER_REFLECT边界元素的镜像。比如: fedcba|abcdefgh|hgfedcb

– cv2.BORDER_REFLECT_101 or cv2.BORDER_DEFAULT 跟上面一样,但稍作改动。例如: gfedcb|abcdefgh|gfedcba

– cv2.BORDER_REPLICATE重复最后一个元素。例如: aaaaaa| abcdefgh|hhhhhhh

– cv2.BORDER_WRAP 不知道怎么说了, 就像这样: cdefgh| abcdefgh|abcdefg

import cv2
import numpy as np
from matplotlib import pyplot as pltBLUE = [255, 0, 0]img = cv2.imread('test.jpg')
replicate = cv2.copyMakeBorder(img, 30, 30, 30, 30, cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img, 30, 30, 30, 30, cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, 30, 30, 30, 30, cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img, 30, 30, 30, 30, cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, 30, 30, 30, 30, cv2.BORDER_CONSTANT, value=BLUE)plt.subplot(231),
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB), 'gray'),   plt.title('ORIGINAL')
plt.subplot(232),
plt.imshow(cv2.cvtColor(replicate,cv2.COLOR_BGR2RGB),'gray'),  plt.title('REPLICATE')
plt.subplot(233), 
plt.imshow(cv2.cvtColor(reflect, cv2.COLOR_BGR2RGB), 'gray'),   plt.title('REFLECT')
plt.subplot(234), 
plt.imshow(cv2.cvtColor(reflect101, cv2.COLOR_BGR2RGB), 'gray'),   plt.title('REFLECT_101')
plt.subplot(235), 
plt.imshow(cv2.cvtColor(wrap, cv2.COLOR_BGR2RGB), 'gray'),    plt.title('WRAP')
plt.subplot(236), 
plt.imshow(cv2.cvtColor(constant, cv2.COLOR_BGR2RGB), 'gray'), plt.title('CONSTANT')
plt.show() 
  • 使用示例:
import cv2 as cv
import matplotlib.pyplot as pltimg2 = cv.imread(r"C:\Users\Administrator\Desktop\dog.jpg")
img = cv.cvtColor(img2,cv.COLOR_BGR2RGB)  #matplotlib的图像为RGB格式
constant = cv.copyMakeBorder(img,20,20,20,20,cv.BORDER_CONSTANT,value=[0,255,0]) #绿色
reflect = cv.copyMakeBorder(img,20,20,20,20,cv.BORDER_REFLECT)
reflect01 = cv.copyMakeBorder(img,20,20,20,20,cv.BORDER_REFLECT_101)
replicate = cv.copyMakeBorder(img,20,20,20,20,cv.BORDER_REPLICATE)
wrap = cv.copyMakeBorder(img,20,20,20,20,cv.BORDER_WRAP)
titles = ["constant","reflect","reflect01","replicate","wrap"]
images = [constant,reflect,reflect01,replicate,wrap]
for i in range(5):plt.subplot(2,3,i+1),plt.imshow(images[i]),plt.title(titles[i])plt.xticks([]),plt.yticks([])
plt.show()

  

相关文章:

  • 学习笔记——网络参考模型——TCP/IP模型(物理层)
  • 常用的 Git 命令
  • C语言练习题之——从简单到烧脑(13)(每日两道)
  • python (pycharm)第五章 面向函数
  • 计算机网络期末复习-计算机网络体系结构第一章(王道25)
  • C++设计模式-状态模式
  • 【文件fd】回顾C语言文件操作 | 详细解析C语言文件操作写w追加a | 重定向和“w““a“
  • HOW - BFF 服务实践系列(一)
  • 探索Python机器学习:从基础到实践
  • Java基础入门day62
  • 【云原生】Kubernetes----POD控制器
  • 编程学习技巧——实战
  • 对boot项目拆分成cloud项目的笔记
  • 如何实现一个AI聊天功能
  • 智能超越了科技,更是一个复杂系统
  • Android框架之Volley
  • Android优雅地处理按钮重复点击
  • docker python 配置
  • javascript数组去重/查找/插入/删除
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Sass 快速入门教程
  • SpiderData 2019年2月13日 DApp数据排行榜
  • Swoft 源码剖析 - 代码自动更新机制
  • 初识 webpack
  • 大快搜索数据爬虫技术实例安装教学篇
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 全栈开发——Linux
  • 如何在 Tornado 中实现 Middleware
  • 使用 Docker 部署 Spring Boot项目
  • 使用Gradle第一次构建Java程序
  • 物联网链路协议
  • 新版博客前端前瞻
  • 学习Vue.js的五个小例子
  • puppet连载22:define用法
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • $(function(){})与(function($){....})(jQuery)的区别
  • (2)STL算法之元素计数
  • (arch)linux 转换文件编码格式
  • (翻译)terry crowley: 写给程序员
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (十六)一篇文章学会Java的常用API
  • (五)Python 垃圾回收机制
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .NET Core 2.1路线图
  • .NET MVC之AOP
  • .net生成的类,跨工程调用显示注释
  • .Net中ListT 泛型转成DataTable、DataSet
  • /etc/motd and /etc/issue
  • @RequestBody与@ResponseBody的使用
  • @Valid和@NotNull字段校验使用
  • [20180312]进程管理其中的SQL Server进程占用内存远远大于SQL server内部统计出来的内存...