计算机视觉(OpenCV+TensorFlow)
文章目录
前言
本系列文章主要介绍计算机视觉(computer version)中用python操作Opencv库,进行cv处理 本篇文章为第一篇,主要讲解cv中的一些简单的概念和基本操作
一. 计算机眼中的图像
当我们使用OpenCV之前,我猜肯定有许多朋友还不知道计算机是如何存储图像的,理解计算机是如何存储图像会帮助我们更好的理解和掌握 OpenCV
这部分的内容参考了下面两篇文章(两位原作者都讲的挺好的,但是每位都好像没有讲清某个点,这里我相当于总和了一下)
计算机中的图像:https://blog.csdn.net/weixin_44191535/article/details/105022799
关于三通道彩色图的存储方式的理解:https://blog.csdn.net/qq_31347869/article/details/90761925
1.像素
像素是组成图像的最小的单位,每个像素点都有一定的属性,记录了图像某个位置的信息,就类似于拼图一样。像素就是拼图的每一个拼图块,把每一个拼图块都按正确的方式拼在一起就形成了一个完整的拼图。那这里每一个拼图块就好像是每一个像素点一样,最后拼好的完整拼图就是图像。那我们可以形象的理解为:将所有的像素都按一定的方式拼在一起后,就形成了图像。
下面的这个小狮子拼图就是一个典型的例子,但是就是每一个拼图块太大了,导致小狮子看起来还是会有拼图块拼接的痕迹。那如果每一个拼图块再小一点,拼图块再多一点,看起来会怎么样呢?
那这张图看起来是不是就好一点了,至少拼接感没有那么强了。那说完拼图,其实图像也是一样,用许多像素拼接起来的。
我们看下面的这张图片,我们看道的每一个小小的方格就是一个像素点,将这些像素点按某种特定方式组合起来就是组成了下面这张图——蜘蛛侠。但是这个蜘蛛侠看起来是不是就有点像我们看上面的小狮子拼图一样。看起来就像是由小方块组成的,这样效果肯定不好,那解决方案就是用更多的像素点来组合
这张是不是就清晰很多了呢?看起来就没有那种拼接肝了,但是我们放大来看,右下角的蜘蛛侠眼中的高楼看起来是不是就有点拼接感了。那这张高清图像由1024000个像素点组成,也怪不得这张图像更清晰,品质更高,这也暗示着,像素点的个数决定了图像的品质
刚才我们说像素点的个数决定了图像的品质,其实现实生活中,我们听到的最多的其实是分辨率,那下面我们就来看看分辨率和像素之间是什么关系。
2.分辨率
分辨率其实就是 单位长度内包含的像素点的数量,单位是 像素/英寸,简写为 ppi (pixels per inch)
举个例子:
11 英寸的图像,器分辨率是30ppi,说明每英寸上有30个像素点,那11的区域中就有900个像素点。
注意:分辨率越高,包含的像素点就越多,图像就越清晰,但是占用的存储空间也会越大。
在了解完图像的构成后,我们也该进行下一步了:了解图像是在计算机中如何存储的
3.灰度图像的存储
众所周知,计算机只能识别0,1这两个数字,同样也只能存储着两个数字。那我们的图像也不例外,在计算机中也是通过0,1来表示的。
我们可以简单的规定,0表示颜色黑,1表示颜色白。但这样就意味着每个像素只能取到黑色或白色这两种颜色。那这样组合成的图像长什么样呢?

上面这张图就是计算中只用0,1来存储的,我们看着是不是很割裂,颜色变化的过于剧烈,没有一种连续的感觉,由于每个像素点的值不是 0 就是 1,所以上诉图像在计算机中是一个二维数组(只截取了一部分)
那为了使得颜色能有一个渐变的效果,我们看下面这个渐变图,黑色和白色之间明显存在一个渐变,而我们现在如果可以让计算机来表示中间的颜色,那图像看起来不就连贯了许多嘛。那如何让计算机表示中间的颜色呢?我们刚才说了,计算机只认识0,1两个数字,那么我们可以用 01的组合来表示颜色,而 01的组合如果看作是二进制,那计算机就可以识别了

下面这张图我们就是将每个颜色用一个十进制的数来表示(计算机存储的时候会将其转为二进制)
这样我们就将黑色和白色之间的颜色划分了 256 个等级,每个等级用一个数值表示,那我们就可以表示处黑白之间所用的颜色了,这样就不存在黑白突变的图像了,而是一种渐变的图像,上述的那朵玫瑰的渐变图像如下图,这也就是我们常说的灰度图。那其实我们也经常把像素点的数值称为该像素点的亮度。
这样的渐变图像在计算机种依旧是数组存储,只不过每个像素点的颜色值是 0-255之间的值而不再只是0或1
我们看下面的图,将玫瑰花的一部分放大来看就是一个个的像素拼接在一起形成的,而这些像素在计算机中是由右边的矩阵表示的,矩阵中每一个数就代表一个像素,而数值则代表了像素的颜色。
通过以上分析,我们可以将黑白图像或灰度图像中每个像素的颜色值一数字的形式存放在二维数组中,计算机通过存储二维数组来存储图像,但问题来了,我们最后想要的肯定是一张彩色的图像,那彩色的图像,计算机是如何存储的呢?
4.彩色图像的存储
在讲解彩色图像的存储中,我们先来回顾一些光的三原色:牛顿发现一束白光通过三棱镜可以分解为 赤,橙,黄,绿,青,蓝,紫七种不同的颜色,后来他又继续分解,发现红绿蓝怎么也没法分解为其他颜色,还有一些其他的知识(简单来说,我不懂😥),总之我们称红绿蓝为光的三原色,用 RGB来表示,这也意味着自然界中人眼的可见光均可以使用三原色叠加而成。
既然可以叠加,那我们就可以将三种原色任意混搭,进而可以形成7种颜色
但 7种 是不足以表示我们这个丰富多彩的世界,我们上面说过黑白之间有渐变,所以我们可以将其分成256种“颜色”,我们也可以在每个原色之间也形成渐变
我们依旧是将每个原色中最暗的部分用 0 表示,最亮的部分用255表示,那我们把 0-255 所表示的含义叫做 色阶。
我们将三原色的混搭立体化展示,每个坐标轴的刻度为 1,范围是0-255,那空间每一个点就表示三原色的混合程度
那理解了彩色的表示,那这些颜色是如何在计算机中存储的呢?在回答这个问题之前,我们还要再理解一个概念:通道
5.通道
彩色图在计算机中存储的时候会分为三层,R层,G层,B层,然后处理的时候会将这三层叠在一起处理,而这三层就叫做通道。但是我觉得这个通道还是有点抽象,通道有一种颜色通过的意思,但是我还是觉得直接用层来解释更好。
那解释完通道后,我们来看一下彩色图片的三个通道

我们可以发现每个通道都是一副完整的灰度图,唯一不同的就是颜色亮暗程度不同。那这是因为
每个通道都记录了对应原色的色阶值。比如:R通道记录了该位置对应像素点的红原色色阶的值。G通道和B通道则是记录了该位置对应像素点的绿原色和蓝原色的色阶值。由于每个通道都包含每个像素的部分颜色值,这样导致的结果是每个通道上的每个像素点只有一个值,如果我们分离出通道,这样每个通道在计算机中的形式就是一个二维数组,那二维数组在计算机中不就是灰度图嘛。
那现在我们应该就理解了颜色的通道了,那可能有朋友会问了,那这几个通道还是灰度图,计算机是怎么把这些灰度图变成最后的彩色图的。
其实我们可以这么来想:我们就把三个灰度图中像素点的数值作为该通道的原色的亮暗程度。也就是说如果我们想要一个图像更红一些,那么就把R通道的像素点的值增加一样。这样最后计算机在处理的时候,会根据各个通道的值“上色”。
下面就是当计算机上色后,组合为最后的彩色图片。

那经过上面的讲解后,我们会很容易想到彩色图在计算机中是用三维数组中存储的。但三维数组,我们平时用的不多,用的最多的还是二维数组,那我们看下面的图更好的理解一下
6.案例
那经过上面的讲解后,相信大家都理解了计算机眼中的图像,那我们就用OpenCV来简单的看一下一个图片
这里我们先导一下包,然后把一个猫的图片导进来,然后我们看一下这个 img
我们先来看一下灰度图的表示
我们看这个灰度图,一个二维矩阵,那我们现在看这个应该就很轻松了把,那三维矩阵其实就是把这个153,变成一个一维数组比如[142,151,160],对把。其实三维矩阵就是把 153换为 [142,151,160]。其实这也好理解。153不是表示亮度嘛,而彩色图每一个像素点要有三原色的色阶(就是三原色的亮度)。那把 153 变为 [142,151,160] 那不就很好理解了嘛。
下面我们来看彩色图的矩阵,现在应该就很好理解了。
二.OpenCV的图像基本操作
学习OpenCV之前,需要大家先了解一下 Numpy 这个库,因为OpenCV中全部把图像解析称 Numpy的ndarray结构了
1.读取图像
import numpy as np
import cv2 #这个cv2就指的是OpenCV,opencv读取的格式是BGR,他不太像我们平常使用给RGB格式
# 这个imread函数就是用来读取一个图像,里面传入图像的路径就可以、
# 这样我们就把cat这张照片读进来了
img=cv2.imread('cat.jpg')
# 我们还可以在路径后传入一个cv2.IMREAD_GRAYSCALE,将读进来的图像转换为灰度图
img=cv2.imread('cat.jpg',cv2.IMREAD_GRAYSCALE)
# 下面我们看这个 cv_show 这个函数,这个函数定义用来用一个窗口展示我们的图像
# name是窗口的名称,img是图像的路径
# 函数cv2.imshow()用来显示图像,但是他一展示图像就直接消失了,我们几乎看不见,所以我们
# 后面还有一个waitKey()函数,用来等用户按下一个键,如果传入的是0或负数,会一直等
# destroyAllWindows()是用来销毁所有的窗口
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
#imwrite()将图像写入到一个图像格式的文件
cv2.imwrite('mycat.png',img)
2.获取,读写数据
# 先读进来一张照片
img=cv2.imread('cat.jpg')
# 我们没有指定灰度图,默认传进来的就是彩色图,那img就是一个三维数组,这里我们选取img的一部分
# 0:50是python的切片,表示取第一维的下标为a1至a2-1的数组切片
# 0:200表示取第二维的下标为b1至b2-1的数组切片。
# 这个操作也被称为ROI区域,就是选取感兴趣的模块,我们要进行检测时,经常会只关心图像内的一部分区域
cat=img[0:50,0:200]
cv_show('cat',cat)
# split函数就是把彩色图的三个通道分开,返回值就是三个通道,但是顺序是b,g,r
# 对于写惯Java的人,这种多个返回值还真的有点不是很适应
b,g,r=cv2.split(img)
# merge函数就是将b,g,r三个通道合并为一个彩色图
img=cv2.merge((b,g,r))
3.数值计算
# 导入两张图片
img_cat=cv2.imread('cat.jpg')
img_dog=cv2.imread('dog.jpg')
# 如果直接在一个图像后面加10,那他会将所以的像素点的值加10
img_cat2=img_cat+10
# 如果让两个图片相加的话,要两张图片的维度都相同,那结果就是对应位置的像素点相加
# 那我们知道,这两个照片都是 ndarray格式,那相加也是Numpy的相加,而这种相加如果
# 最后的结果大于255的话,会%255
(img_cat + img_cat2)[:5,:,0]
# OpenCV的加法如果最后大于255了。那就取255
cv.add(img_cat + img_cat2)
4.图像融合
# 导入两张图片
img_cat=cv2.imread('cat.jpg')
img_dog=cv2.imread('dog.jpg')
# 我们刚才说加法的时候,谈过要两张图片的维度一样才能相加,图像融合也是一样的
# 所以我们要先用resize()函数将图片的维度调整一致
# 第一个参数是要调整的图像,第二个参数是要调整到多大
# 这个函数不是通过切分达到效果,而是通过放缩到达效果
img_dog=cv2.resize(img_dog,img_cat.shape)
# addWeighted()函数就是用来融合两张图像,0.7和0.3是图片的权重,
# 最后一个 0 ,是两个图片叠加后,再加0,相当于一个偏置项
res = cv.addWeighted(img_dag,0.7,img_cat,0.3,0)
5.图像阈值(二值化)
# 当我们希望图像中的像素点位于一个指定的区间,这时候我们可以用阈值操作
# OpenCV中的阈值操作是 ret,dst=threshold(src,thresh,maxval,type)
# src就是原始图像,只能输入单通道图像,那就是灰度图
# thresh就是阈值
# maxval:当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
# type:二值化操作的类型,包含以下5种类型:
# cv2.THRESH_BINARY:超过阈值的值取maxval,没有超过的取0
# cv2.THRESH_BINARY_INV
# cv2.THRESH_TRUNC:大于阈值部分设为阈值,否则不变
# cv2.THRESH_TOZERO:大于阈值部分不改变,否则设为0
# cv2.THRESH_TOZERO_INV
# dst是输出图
# 那这个就是把超过127的值变成255,没超过的变成0
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
# 那这个就是把超过127的值变成0,没超过的变成255
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
# 那这个就是把超过127的值变成255,没超过的还是原来的值
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)
# 那这个就是把超过127的值还是不变,没超过的变成0
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
# 那这个就是把超过127的值变成0,没超过不变
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)
总结
本篇文章后,大家应该对计算机视觉应该有了一个比较初步的认知,下一篇文章中我们将继续深入CV的世界
我是Mayphry,从一点点到亿点点,我们下次再见
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)