第一节,初识OpenCV3-图像的读、写、显、格式转化等

之前一直在看深度学习,突然用到了对图像处理的知识,所以过来补充一下OpenCV基础。

就顺便从网上了买了一本OpenCV 3计算机视觉这本书,这本书比较薄,但是目前已经够我用了,在这里就记录一下我的学习笔记。

一 OpeCV3的安装

在前面我已经介绍过我的python运行环境,是运行在windos 7操作系统下,安装的Anaconda集成开发环境。具体安装步骤可以参考文章第一节,windows下深度学习theano环境搭建

下面介绍一下如何在Anaconda命令窗口下安装opencv:

conda install -c https://conda.binstar.org/menpo opencv 

打开Anaconda Prompt,输入python,进入python环境之后,import cv2。不报错,说明安装成功。

二 OpenCV基础

主要介绍下面7块内容:

  • 读写图像文件
  • 图像与原始字节之间的转换
  • 使用numpy.array访问图像数据
  • 视频文件的读写(复制视频)
  • 捕获摄像头的帧,并保存视频文件
  • 窗口显示图像
  • 在窗口显示摄像头帧

为了做测试,我专门创建了一个OpenCV文件夹,在下面放一些测试代码,并创建了两个文件夹,叫做video和image分别保存一些视频和图像文件:

  第一节,初识OpenCV3-图像的读、写、显、格式转化等

第一节,初识OpenCV3-图像的读、写、显、格式转化等

第一节,初识OpenCV3-图像的读、写、显、格式转化等

1.读写图像文件

OpenCV的imread()和imwrite()函数能支持各种静态图像文件格式。不同系统支持的文件格式不一样,但是都支持bmp格式,通常还支持 png,jpeg,tiff格式文件 :

  • bmp格式:每个像素每个通道为8位;
  • png:每个像素每个通道为8位或者16位。

(1) 使用函数cv2.imread(filename[,flags])读入图像

该函数返回我们读取的图像数据:

  • 第一个参数,这幅图像应该在此程序的工作路径,或者给函数提供完整路径;
  • 第二个参数是标志位,要告诉函数应该如何读取这幅图片,即指定加载图片的颜色类型,默认加载类型是cv2.IMREAD_COLOR:
  1. cv2.IMREAD_COLOR=1:读入一副彩色图像,将图像转化为三通道BGR彩色图像。图像的透明度会被忽略,这是默认参数;
  2. cv2.IMREAD_GRAYSCALE=0::将加载的图像转换为单通道灰度图;
  3. cv2.IMREAD_UNCHANGED = -1:已经废除,不再使用;
  4. cv2.IMREAD_ANYDEPTH=2:若载入图像深度为16位或32为就返回其对应深度,否则将图像转换为8位图像;
  5. cv2.IMREAD_ANYCOLOR=4:保持图像原格式,可以读取任意可能的彩色格式;
  6. cv2.IMREAD_LOAD_GDAL=8:使用文件格式驱动加载图像,在现阶段用处不多。

在使用flags时可能会同时使用多种flags,如果发生冲突,函数将自动采用较小数字值对应的加载方式。如:cv2.IMREAD_COLOR | cv2.IMREAD_ANYCOLOR,则imread()函数将自动载入cv2.IMREAD_COLOR所对应的3通道彩色图。如果要载入图像原本的彩色格式和深度,则可以使用: cv2.IMREAD_ANYCOLOR | cv2.IMREAD_ANYDEPTH。
也可以利用flags是int类型的变量输入其他值以达到加载特定图像格式的目的,但符合一下标准:

  • flags > 0:返回一个三通道的彩色图像;
  • flags = 0: 返回灰度图像 ;
  • flags < 0: 返回包含Alpha通道的图像。

图像在默认情况下不是从Alpha通道进来的,如果需要载入Alpha通道的话就取负值。

(2)使用函数cv2.imwrite(filename,img[,params])

该函数用于保存图像:

  • 第一个参数,这幅图像应该保存的工作路径,或者给函数提供完整路径;
  • 第二个参数是输入图像;
  • 第三个参数表示为特定保存格式的参数编码,在一般情况下不需要更改;

(3)使用cv2.cvtColor(src,code[,dst[,dstCn]])函数

用于格式转换。输出为输出图像即进行颜色空间变换后存储图像。我们生活中大多数看到的彩色图片都是BGR类型,但是在进行图像处理时,需要用到灰度图、二值图、HSV、HSI等颜色制式,opencv提供了cv2.cvtColor()函数来实现这些功能:

  • 第一个参数输入图像即要进行颜色空间变换的原图像;
  • 第二个参数转换的代码或标识,即在此确定将什么制式的图片转换成什么制式的图片;
  • 第三个参数为为输出图像即进行颜色空间变换后存储图像;
  • 第四个参数是目标图像通道数,如果取0,则由src和code决定;

函数的作用是将一个图像从一个颜色空间转换到另一个颜色空间,但是从BGR向其他类型转换时,必须明确指出图像的颜色通道,在opencv中,其默认的颜色制式排列是BGR而非RGB。所以对于24位颜色图像来说,前8-bit是蓝色,中间8-bit是绿色,最后8-bit是红色。常见的B,G,R通道的取值范围为:

  • . 0-255 :cv2.CV_8U类型图片
  • . 0-65535: cv2.CV_16U类型图片
  • . 0-1::cv2.CV_32F类型图片

讲到这里有必要介绍一下图像的数据类型,一张图片就是一个简单的numpy数组,数组的数据类型有很多种,相互之间也可以转换,这些数据类型以及取值范围如下表所示:

Data type Range
uint8 0 to 255
uint16 0 to 65535
uint32 0 to 232
float32 -1 to 1 or 0 to 1
int8 -128 to 127
int16 -32768 to 32767
int32 -231 to 231 - 1
 

一张图片的像素值范围是[0,255],因此默认类型是uint8。在上面表中需要注意的是float32类型,它的范围是[-1,1]或[0,1]之间。详细内容可以参考:python数字图像处理(4):图像数据类型及颜色空间转换

对于线性变换来说,这些取值范围是无关紧要的。但是对于非线性转换,输入的BGR图像必须归一化到其对应的取值范围来或得最终正确的转换结果,例如从BGR>L*u*v转换。如果从一个8-bit类型图像不经过任何缩放(scaling)直接转换为32-bit浮点型图像,函数将会以0-255的取值范围来取代0-1的取值范围,所以在使用cvtColor函数之前需要对图像进行缩放如下:

img *= 1.0/255;
cvtColor(img,cv2.COLOR_BGR2Luv);

如果对8-bit图像使用cvtColor()函数进行转换将会由一些信息丢失。函数可以做下面类型的转换,需要说明的是在opencv2.x时颜色空间转换code用的宏定义是CV_前缀开头,而在opencv3.x版本其颜色空间转换code宏定义更改为COLOR_开头,而经验证,2.4.13版本中opencv同事支持这两种形式的写法。故下面表格会将两种code类型同时列出,以供参考:

第一节,初识OpenCV3-图像的读、写、显、格式转化等

这里列出的类型并不齐全,但是对于一般的图像处理已经够用。需要特别说明的是BGR–>GRAY的转换是我们常用的转换格式,其转换公式如下:

第一节,初识OpenCV3-图像的读、写、显、格式转化等

介绍一下BGRA格式图片,BGRA是代表Blue(蓝色),Green(绿色)、Red(红色)、和Alpha的色彩空间。虽然它有时候被描述为一个颜色空间,但是它其实是BGR模型附加了额外的信息,可以属于任何一种BGR颜色空间。Alpha参数一般用作不透明度参数,如果一个像素的alpha通道数值为0%,那它就是完全透明的也就是肉眼不可见,而数值为100%则意味着一个完全不透明的像素,传统的数字图像就是alpha值为100%.

import cv2
import numpy as np
import os '''
1.读写图像文件
''' #通过二维numpy数组创建一个黑色的正方形图像
img = np.zeros((3,3),dtype=np.uint8) #输出 每一个像素都是8位整数 范围0-255 图片大小为3x3
print(img) #[[0 0 0]
# [0 0 0]
# [0 0 0]] print(img.shape) #(3, 3) #利用cvtColor()函数将图像转换为blue-green-red(BGR)格式
img = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR) #输出维度为3x3x3 表明图像大小为3x3 有三个通道 每个像素由一个三元数组表示(B,G,R)
print(img) # [[[0 0 0]
# [0 0 0]
# [0 0 0]] # [[0 0 0]
# [0 0 0]
# [0 0 0]] # [[0 0 0]
# [0 0 0]
# [0 0 0]]] print(img.shape) #(3, 3, 3) #把png图像转换为jpeg格式
root = '.\image'
file = os.path.join(root,'test.bmp') #加载OpenCV图像最简单的方式是使用imread函数,该函数会返回一副图片,这幅图片是一个数组(根据imread()输入参数的不同,该图像可能是一个二维数组,也可能是三维数组)
#在默认情况下,即使文件是灰度格式,imread()函数也会返回BGR格式的图像
#BGR和RGB所表示的色彩空间相同,字节顺序相反
img = cv2.imread(file) #读bmp格式取图片 cv2.imwrite('test.jpg',img) #保存成jpg格式 #按照指定参数读取图片 加载bmp文件作为灰度图像(丢失颜色信息)
gray_image = cv2.imread(file,cv2.IMREAD_GRAYSCALE)
#保存为灰度的jpg图像
cv2.imwrite('test_gray.jpg',gray_image)

2.图像与原始字节之间的转换

一个OpenCV图像一般是numpy.array类型的二维或者三维数组。8位的灰度图像是一个含有字节值得二维数组,维度为 height x width,24位的BGR是一个三维数组 维度为height x width x channel。

(1)使用函数cv2.imshow(winname,mat)

该函数用来显示图像,窗口会自动调整为图像大小:

  • 第一个参数是窗口的名字。
  • 第二个参数是要输出的图像。

imshow()在用于指定的窗口显示图像时,如果窗口用cv2.WINDOW_ATTOSIZE创建,那么显示图像原始大小。否则将图像进行缩放以适合窗口。而imshow()函数缩放图像取决与图像深度:

  • 如果载入图像是8位无符号类型(8-bis unsigned),就显示图像本身。
  • 如果图像是16位无符号类型(16-bist unsigned)或32位无整型(32-bit integer),则使用像素值除以256.也就是说将像素值范围在[0,255x266]之间的元素映射到(0,255]范围内。
  • 如果载入图像是32位浮点型(32-bit floating-point),像素值要乘以255.也就是说像素值范围在[0,1]映射到[0,255].
(2)cv2.waitKey() 
键盘绑定函数。需要指出的是它的时间尺度是毫 秒级。函数等待特定的几毫秒,看是否有键盘输入。特定的几毫秒之内,如果 按下任意键,这个函数会返回按键的ASCII 码值,程序将会继续运行。如果没 有键盘输入,返回值为-1,如果我们设置这个函数的参数为0,那它将会无限期的等待键盘输入。它也可以被用来检测特定键是否被按下。
'''
2.图像与原始字节之间的转换
''' img = cv2.imread(file) #读bmp格式取图片
print('图像{0}的维度为{1}'.format(file,img.shape))
#显示的转换为一维的python bytearray格式
byte_array = bytearray(img) #图像.\image\test.bmp的维度为(45, 50, 3)
print('转换之后的维度为:',len(byte_array),type(byte_array)) #转换之后的维度为: 6750 <class 'bytearray'> #bytearray含有恰当顺序的字节,可以通过显示转换和重构 得到numpy.array形式的图像
img = np.asarray(byte_array).reshape(45,50,3)
cv2.imshow('window 1',img)cv2.waitKey(10) #等候10ms '''
一个详细的例子 将含有随机字节的bytearray转换为灰度图像和BGR图像
''' #随机生成120,000个字节的数组
random_byte_array = bytearray(os.urandom(120000)) #生成随机字节
#转换成numpy.array类型
byte_array = np.asarray(random_byte_array,dtype=np.uint8) #把数组转换成 400 x 300的灰度图像
gray_image = byte_array.reshape(300,400)
cv2.imwrite('test_gray.png',gray_image) #把数组转换成 400 x 100的BGR图像
bgr_image = byte_array.reshape(100,400,3)
cv2.imwrite('test_bgr.png',bgr_image)

3.使用numpy.array访问图像数据

下面实现把图像的一部分复制到另一个位置,然后显示。

'''
3.使用numpy.array访问图像数据
'''
#将BGR图像在(0,0)处的像素转化为白像素
img = cv2.imread('test.jpg')
img[0,0] = [255,255,255] #img[0,0]操作和下面操作一样
'''
numpy.array的item(n_hight,n_width,n_channel)方法可以获取指定索引的值 通道BGR依次对应索引0,1,2
'''
print(img.item(25,26,0)) #获取25列26行B通道的值 222
img.itemset((25,26,0),155) #设置新的值
print(img.item(25,26,0)) # #将指定通道的所有值置为零
img[:,:,1] = 0 #将G通道像素值全部置为零 '''
1.读取感兴趣区域,并把值赋值给一个变量
2.同理设定第二个区域,赋值给另一个变量
3.将第一个我区域的值赋值给第二个区域 (将图像一部分拷贝到图像另一部分)
'''
img = cv2.imread('./image/img1.jpg')
my_roi = img[800:1000,300:500]
img[0:200,200:400] =my_roi
cv2.imshow('window 2',img)
cv2.waitKey(10) #等候10ms
print(img.shape) #(1080, 1920, 3) 图像的高度,宽度,和通道数
print(img.size) #36220800 图像的大小
print(img.dtype) #uint8 图像的数据类型

4.视频文件的读写(复制视频)

OpenCV提供了VideoCapture类和VideoWriter类来支持各种格式的视频文件。支持的格式类型会因系统的不同而变化,但是都支持avi格式,在到达视频文件末尾之前,VideoCapture可通过read()函数来获取新的帧,每一帧都是一幅基于BGR的图像。VideoWriter可以通过write()写入每一帧图像。

'''
4.视频文件的读写(实现视频的复制)
''' '''
将一幅图像传递给VideoWriter类的write()函数,该函数会将这幅图像加到VideoWriter类所指向的文件中
'''
video_capture = cv2.VideoCapture('./video./2.mp4')
#获取帧速率
fps = video_capture.get(cv2.CAP_PROP_FPS)
print('该视频的帧速率为:',fps) #该视频的帧速率为: 30.009902511911992
#获取图片的宽和高
size =(int(video_capture.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
print('该视频每一帧的大小为:',size) #该视频每一帧的大小为: (544, 960) #1.VideoWriter类的构造函数指定视频文件名,这个文件名对应的文件若存在,则会被覆
#2.需要指定编解码器
#3.帧速率
#4.帧大小
video_writer = cv2.VideoWriter('./video/4.avi',cv2.VideoWriter_fourcc('m','p','','v'),fps,size) success,frame = video_capture.read()
cv2.imwrite('./video/frame.jpg',frame)
#读取帧,直至没有帧可以读取
while success:
#写入一帧
video_writer.write(frame)
#读取一帧
success,frame = video_capture.read()
video_writer.release()
video_capture.release()

5.捕获摄像头的帧,并保存视频文件

VedioCapture类可以获得摄像头的帧流,但对于摄像头而言,通常不是用视频的文件名来构造VideoCapture类, 而是需要传递摄像头的设备索引,VideoCapture类对象的get方法不能返回摄像头帧速率的准确值,它总是返回0。

'''
5.捕获摄像头的帧(保存视频文件)
'''
#获取摄像头10s的视频信息,并将其写入一个avi文件中
camera_capture = cv2.VideoCapture(0) #OpenCV没有提供任何查询摄像头数量和属性的方法。如果使用无效的索引构造了VideoCapture类,就得不到帧,read方法
#就会返回 False,None 因此需要在读取前判断一下设备是否已经打开
if camera_capture.isOpened():
fps = 30 #假设帧大小
#获取图片的宽和高
size =(int(camera_capture.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(camera_capture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
#1.VideoWriter类的构造函数指定视频文件名,这个文件名对应的文件若存在,则会被覆
#2.需要指定编解码器
#3.帧速率
#4.帧大小
video_writer = cv2.VideoWriter('./video/camera.avi',cv2.VideoWriter_fourcc('m','p','','v'),fps,size) success,frame = camera_capture.read()
num_frame_remaining = 10*fps - 1 #当前剩余捕获图像个数
while success and num_frame_remaining > 0 and cv2.waitKey(1)==-1:
#显示拍照
cv2.imshow('frame',frame)
#写入一帧
video_writer.write(frame)
#读取一帧
success,frame = camera_capture.read()
num_frame_remaining -= 1 #释放
camera_capture.release()
video_writer.release()

6.窗口显示图像

'''
6 窗口显示图像
imshow()函数可以用来实现图片,但是这个图片显示出来后会立即消失,一般我们需要使用waitKey()函数,
传入等待键盘触发的时间,单位为ms,返回值为-1(表示没有键按下)或者ASCII码。并且需要调用destoryAllWindows()
释放OpenCV创建的所有窗口
'''
img = cv2.imread('./image/img2.jpg')
cv2.imshow('window 3',img)
cv2.waitKey(10)

7.在窗口显示摄像头帧

以下代码实现实时捕获摄像头的帧并显示在窗口,按任何键可以退出窗口。

(1)cv2.destroyAllWindows() 

该函数可以轻易删除任何我们建立的窗口。如果你想删除特定的窗口可以使用cv2.destroyWindow(),在括号内输入你想删除的窗口名。

(2)cv2.namedWindow(winname[,flags])

指定窗口名来创建窗口:

  • 第一个参数为窗口的名字。
  • 第二个参数为窗口属性。

flags是一个枚举类型,其由如下参数:

  • cv2.WINDOW_NORMAL:可以改变窗口大小(无限制),也可将一个满屏窗口转换成常用大小;
  • cv2.WINDOW_AUTOSIZE:程序会根据呈现内容自动调整大小且不能手动更改窗口大小;
  • cv2.WINDOW_OPENGL:创建支持OpenGL的窗口;
  • cv2.WINDOW_FULLSCREEN:创建一个充满屏幕的窗口;cv2.WINDOW_FREETATIO:图像将尽可能展开;
  • cv2.WINDOW_KEEPRATIO:图像比例受到约束。

namedWindow()函数是通过指定的名字创建一个作为图像和进度条显示的窗口,如果有相同名称的窗口已经存在,则函数不会重复创建窗口,而是什么都不做。我们可以调用destroyWindows()或者destroyAllWindows()函数来关闭窗口并取消之前分配的与窗口相关的所有内存空间。

'''
7.在窗口显示摄像头帧
'''
clicked = False
def on_mouse(event,x,y,flag,param):
'''
#鼠标事件的回调函数 args:
event:回调事件参数,有很多取值,分别对应不同的鼠标事件
param:可选参数,它是setMouseCallback()函数的第三个参数 默认为0
flag:标志参数 如 cv2.EVENT_FLAG_LBUTTON:该事件对应按下鼠标左键
x,y:鼠标坐标
'''
global clicked
#鼠标左键松开
if event == cv2.EVENT_LBUTTONUP:
clicked = True camera_capture = cv2.VideoCapture(0)
#指定窗口名来创建窗口
cv2.namedWindow('window 4')
#设置鼠标事件回调函数 来获取鼠标输入
cv2.setMouseCallback('window 4',on_mouse) print('Showing camera feed.Click window or press any key to stop')
success,frame = camera_capture.read()
#OpenCV窗口只有在调用cv2.waitKey()函数时才会更新,并且waitKey()函数只有在OpenCV窗口成为活动窗口时,才能捕获输入信息
while success and cv2.waitKey(1) == -1 and not clicked:
cv2.imshow('window 4',frame)
success,frame = camera_capture.read() camera_capture.release() cv2.waitKey(10) #等候10ms
cv2.destroyAllWindows() #销毁所有窗口
上一篇:Netty高性能之Reactor线程模型


下一篇:P 1080 Human Gene Functions