OpenCV笔记

总参考1 这是很不错的官方文档。
总参考2 最好是通过谷歌来查里面想要的官方教程。
总参考3 中文翻译的博客。

x、安装:

《python安装》
源码路径:如果用pycharm通过ctrl+左键 到达的 ~/.PyCharm2018.2/system/python_stubs/677015232/cv2/里面实际都是引用,没有真正的代码。这个地址是python2安装的目录下才有真正的代码 ~/anaconda2/lib/python2.7/site-packages/cv2/ ,但你会发现只有一个库 cv2.so ,我猜测里面是c++写好了编译出来的。python只能调用这个库,不能改源码。
(1)普通版本: 参考
pip install opencv-python
(2)Contrib版。不稳定的东西放到OpenCV_contrib里了,普通版本是没有的。 参考
pip install opencv-contrib-python
比如该版本才有引导滤波等板块:
from cv2.ximgproc import guidedFilter

《源码安装》
可以考虑用同事推荐的 opencv 2.4.13版本 或者现在网上的 3.x 版本。
github官方源码

x、报错及解决办法

(1)RuntimeWarning: overflow encountered in ubyte_scalars 像素加减运算溢出异常 参考
原因:ubyte类型数据范围为0~255,若做运算出现负值或超出255,则会抛出异常。
解法:转换下为int16或浮点就行了。

1、读取图片 imread

(1)Python
参考
img = cv2.imread(‘messi5.jpg’,0) # 0代表灰度模式,每个像素有一个0(黑色)到255(白色)之间的亮度值 灰度概念参考。 默认是1 代表以RGB格式读取。-1代表以原始图像读取(包括alpha通道,即透明度的通道)。

如果报错:Premature end of JPEG file 这很可能是图片损坏了的问题,因为不能try catch解决这错误。目前我自己的解决办法是:打印日志或 print 图片名字出来,然后就对应地删除图片。

(2)C++
参考1 参考2
C++: Mat imread(const string& filename, int flags=1 ) 返回的数据结构是Mat,如果想获取图片大小,可以用

width = img.rows;
height = img.cols;

2、导出图片 imwrite

(1)基本知识
np.unique(picture) # 如果 0 代表黑色,255代表高亮
cv2.imwrite("~/my_picture.jpg", picture*255) # 上面只有0 1两种数值时,就需要乘以255。

如果返回False,很可能是不能写 ~ 等的相对地址或简称,一定要绝对地址。
(2)报错:
could not find a writer for the specified extension in function imwrite_
这是因为输出地址的后缀名需要合理。估计会根据后缀名来选择压缩图片策略。

3、一张图像就是NumPy中的数组

OpenCV for Python就是通过NumPy进行绑定的,一张图像就是NumPy中的数组。所以创建图片就是创建np数组。
参考

canvas = np.zeros((300,300,3),dtype=“uint8”) # 全黑的图片
canvas = 255 * np.ones((300,300,3),dtype=“uint8”) # 全白的图片

(1)获取一张图片的大小

参考

print(img.shape) # 格式:(高, 宽, 通道数)

4、显示图片

(1)cv.WaitKey(delay=0) 参考
waitKey()函数的功能是不断刷新图像,频率时间为delay,单位为ms。输入任意键退出,返回值与输入值相同。
delay 用于视频模式,显示完一帧图像后程序等待"delay"ms再显示下一帧视频。显示图片直接=0就好。

img = cv2.imread("D:\\cat.jpg")
cv2.namedWindow("Image")
cv2.imshow("Image", img.astype(np.uint8)) # 如果本来就是uint8就不用astype,否则图片会全花。
cv2.waitKey(0) # 如果不加,在IDLE中执行窗口直接无响应。在命令行中执行的话,则是一闪而过。
cv2.destroyAllWindows() # 释放窗口是个好习惯。

5、读取视频抽帧 cv2.VideoCapture()

参考1 这是很不错的官方文档。
参考2 中文翻译的博客。

cap = cv2.VideoCapture(video_path)
cap.set(1, 0) # 调整第1个属性(控制开始帧),从第0帧开始。具体属性参考官方文档。
res, frame = cap.read() # 第一个是返回True/False的,第二个是当前帧。然后可以不断执行本语句来获取下一帧。
# 随机抽帧的代码实现:
cap = cv2.VideoCapture(vpath)
cap.set(2, 1)  # 到达最后一帧,cap.get(1)获取最大多少帧。
choose =  np.random.randint(int(cap.get(1)))
cap.set(1, choose)  # 读取随机的一帧。
res, frame = cap.read()

下面是常用的几个属性,从0开始数起。
CV_CAP_PROP_POS_MSEC 当前位置视频的毫秒数,第0号属性。
CV_CAP_PROP_POS_FRAMES 下一个被获取的帧的数目是多少。
CV_CAP_PROP_POS_AVI_RATIO 视频的相对位置: 设为0是开始视频位置, 1是结束视频位置。我实践发现,无论 set 该属性成任何值,用get它出来一直都是0.001,不过前两个属性是改变了。所以我通常只会在需要视频末尾时就设为1。
CV_CAP_PROP_FRAME_WIDTH 在视频流中的帧的宽度
CV_CAP_PROP_FRAME_HEIGHT 在视频流中的帧的高度
CV_CAP_PROP_FPS 帧的速率
CV_CAP_PROP_FOURCC 4字节的编码解码器
CV_CAP_PROP_FRAME_COUNT 视频文件的总帧数


注意:
cap.set(2, 1) # 到达最后一帧,cap.get(1)获取最大多少帧。
第一种方法是滑到最后再获取下一帧的编号(总帧数是就是等于这个数值,帧数是从0开始算的),第二种从头不断 cap.read() 获取帧数是有写差别的,还有一种是cap.get(7) 获取总帧数。
实践发现:第一种获取的都是可行的,它得到的总帧数有时比后两种的小。第二、三种方法的结果是一致的,但是后面的帧有些打印出来是不可行的。虽然想统一用第一种方法,数据不会乱的,抽出的帧也不会不可行。 具体例子是手掌分割的:1301230091-6550160311243977227 视频。但是后面发现,有一个视频,使用第一种方法得到的只有很少的帧,后面可行的都找不到。所以现在只好统一使用第二种方法,加上查错的函数。查错函数:imread()读取图片,后看能返回大小属性与否 h, w, c = img.shape。

6、写出视频 cv2.VideoWriter()

拓展:关于视频格式转换问题,可以留意用 FFmpeg。这是因为FFmpeg可以转化音视频为流,能让用户访问几乎所有视频格式。例子:转为支持浏览器播放的视频格式,命令 ffmpeg -i one_video.mpy one_video_copy.mpy

cv2.VideoWriter([filename, fourcc, fps, frameSize[, isColor]])

参数解析:
filename:导出的视频名字。
fourcc:视频数据流格式的四字符代码。看总参考2里面,Fedora最好用XVID,Windows用DIVX。例子:cv2.VideoWriter_fourcc(*'XVID')
fps:帧率。每秒播放多少帧。如果是已经存在的视频,可以用上面的CV_CAP_PROP_FPS获取原始帧率。
frameSize:每帧的大小。先宽后高 (img_width, img_height)。
isColor:可选参数。是否写出彩色视频。默认就好了。
例子:
writer = cv2.VideoWriter(out_video_name, cv2.VideoWriter_fourcc(*'XVID'), fps, (size_output_w, size_output_h))

报错
(1)fourcc 数据流格式问题

OpenCV: FFMPEG: tag 0x44495658/'XVID' is not supported with codec id 12 and format 'mp4 / MP4 (MPEG-4 Part 14)'
OpenCV: FFMPEG: fallback to use tag 0x7634706d/'mp4v'

解决方案:上面的意思是使用XVID 格式不支持 .mp4 后缀的输出,它推荐使用mp4v。

(2)

cv2.error: OpenCV(3.4.3) /io/opencv/modules/videoio/src/cap_ffmpeg.cpp:296: error: (-215:Assertion failed) image.depth() == CV_8U in function 'write'

解决方案:CV_8U 是 uint8 的别名。image.depth() 代表图片里的元素是多少位的。普通见的RGB图片就是0~255的离散整型值,所以控制每张图片的格式是uint8就对了。实践发现,RGB图片,imwrite 写出的时候虽然可以是float64浮点,但是读进来后,依旧是uint8的。注意uint8 最大是255,所以运算时需要转为浮点,避免溢出。我怀疑imwrite 写图片会自动处理转浮点为uint8。

7、改变图片大小 cv2.resize()

cv2.resize(src,dsize,dst=None,fx=None,fy=None,interpolation=None)

参数:

scr:原图
dsize:输出图像尺寸
fx:沿水平轴的比例因子
fy:沿垂直轴的比例因子
interpolation:插值方法,共有5种:
1)INTER_NEAREST - 最近邻插值法。在变换0 1的mask时使用。
2)INTER_LINEAR - 双线性插值法(默认)。在变换原图的时候使用,耗时比上面的多一些。
3)INTER_AREA - 基于局部像素的重采样
4)INTER_CUBIC - 基于4x4像素邻域的3次插值法
5)INTER_LANCZOS4 - 基于8x8像素邻域的Lanczos插值

例子:

res = cv2.resize(img,dsize=(2*width,2*height),interpolation=cv2.INTER_CUBIC)

8、图像的阈值处理cv2.threshold()

参考博客、官方说明、参考中文官方
retval, dst = cv.threshold( src, thresh, maxval, type[, dst] )
参数

src : 原图像
thresh : 进行分类的阈值
maxval : 高于(低于)阈值时赋予的新值
type : 一个方法选择参数

返回:retval 是用于Otsu’s二值化的,可不用。dst 是阈值化后的图像。

例子

retval, dst = cv2.threshold(img,127,255,cv2.THRESH_BINARY)

9、在图片上画矩形框、写文本

参考博客

不使用opencv画图也是可以的,可以自行搜索“python 画矩形框”,用matplotlib等工具。

(1)cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]]) → None 官方文档
输入参数分别为:图像、左上角坐标(先width,后hight)、右下角坐标、颜色数组、粗细
(2)cv2.putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]]) → None 官方文档 字体集
输入参数分别为:图像、文本、位置、字体、大小、颜色数组、粗细

# 画矩形框
cv2.rectangle(img, (width_1, hight_1), (width_2, hight_2), (0,255,0), 4)
# 写文本上去
font = cv2.FONT_HERSHEY_SIMPLEX
text = '001'
cv2.putText(img, text, (212, 310), font, 2, (0,0,255), 1)
# 此时的img就已经被改变了。

10、画圆圈或点

参考博客、官方文档 、C语言的博客,但参数解析很全

两者是同一个函数,点是圆圈的一种特殊情况,即半径很小的圆。

输入参数分别为:图像、圆心坐标(先width,后hight)、半径、颜色、粗细

cv2.circle(img, center, radius, color[, thickness[, lineType[, shift]]]) → img
cv2.circle(img, (x, y), 1, (255, 0, 0), 2)  # 半径为1,是点来的。

11、填充多边形

如果只是画多边形的话,而不用填充的话,应该也有函数。不过目前我是直接用画直线,进行拼装。

参考博客1、参考博客2 、opencv官方
cv2.fillPoly 和 cv2.fillConvexPoly 分别是:非凸任意形状填充和凸填充

tmp = np.zeros(mask.shape, dtype=np.uint8)
box = np.array([[left_top_x, left_top_y], [right_top_x, right_top_y], [right_bottom_x, right_bottom_y], [left__bottom_x, left__bottom_y]]) #  分别是左上、右上、右下、左下。
cv2.fillConvexPoly(tmp, box, (1, 1, 1))  # 直接截取该框。此时mask被改变了。
tmp = cv2.multiply(mask, tmp)
ratio = 1.0 * np.sum(tmp) / (rect_area * 3)  #  计算mask占据这个斜矩形的比例。

12、引导滤波

博客1、博客2-提到快速引导滤波、官方github 、opencv官方

out_1 = guidedFilter(guide=raw, src=pred,  # 使用原图指导,改变 pred 的mak图。当然,也可以原图指导改变原图。
         radius=radius,  # 半径。如果调得大,就会把整个人的边缘都画出来了。建议 [2, 4, 8, 64] 看看。
         eps=eps,  # 引导核的正则化项。调得越大约模糊,去燥程度更大吧。 建议 [1, 1000] 看看。
         dst=None,  # 输出的图片。我觉得out_1 应该就是这个。
         dDepth=None,  # 输出图片的深度,应该是多少位的意思,如uint8。
)

13、双边滤波

理论:wiki理论-很清晰
调用:c代码但能改-里面还有中值滤波、opencv官方、更多的保边滤波器
代码:博客重写官方源码、opencv官方源码、对opencv官方源码做了注释

个人已经重写代码python代码,有效果,但是不够引导滤波的好。

14、边缘检测-Sobel算子

参考1 参考2 参考3

第一个链接有说:數學上為兩者平方後相加,得到梯度強度(G),實際上由於運算效率上的考量,OpenCV預設將G設為Gx和Gy的絕對值相加。直接相加就是第二个链接的方法。参考3 可以看到x、y各个方向的可视化。

相关的知识点–因为用的少就不独立出来了。
(1)cv2.convertScaleAbs()函数:是乘以Scale,加上某个常数,最后取绝对值的操作,返回uint8格式。参考博客 参考官方
(2)cv2.addWeighted()函数:进行两个矩阵的带权。与alpha blending有共同的效果。参考官方
(3)cv2.cvtColor()函数:可以灰度图、RGB图、HSV等等格式的两两转换。 参考博客 参考官方

例子:
img = cv2.imread(rawPath, 0)  # 可以是灰度图,也可以是BGR图。看网上用得多是灰度图。

x = cv2.Sobel(img, cv2.CV_16S, 1, 0)  # 求x轴的梯度
y = cv2.Sobel(img, cv2.CV_16S, 0, 1)  # 求y轴的梯度

absX = cv2.convertScaleAbs(x)  # 转回uint8格式。
absY = cv2.convertScaleAbs(y)

dst = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)

cv2.imwrite(outPath, dst)

15、边缘检测-Canny算子

参考博客1 参考博客2 参考官方

参数
void cv::Canny ( InputArray image, //输入图像,要求为灰度图
OutputArray edges, //canny检测后的输出图像
double threshold1, //阈值1,低阈值
double threshold2, //阈值2,高阈值,低/高=比值1:2~1:3尤佳
int apertureSize = 3, //Sobel算子的孔径大小
bool L2gradient = false //计算梯度幅度值的标识,默认为false
)

img = cv2.imread("D:/lion.jpg", 0)  # 可以是灰度图,也可以RGB图。

img = cv2.GaussianBlur(img,(3,3),0)  # 高斯去燥。不去噪就更多边缘出来,自己可以试试。
canny = cv2.Canny(img, 50, 150)  # 后面是两个阈值。用于控制最小的边缘值,在这两个阈值之间。
 
cv2.imshow('Canny', canny)

16、图像扩边

用途:比如卷积操作等因为边缘点会导致窗口越界,所以就可以做个图像扩边前处理。

参考1 参考2

copyMakeBorder(src, dst, top, bottom, left, right, borderType)
参数说明:
src输入图像
top,bottom,left,right对应边界的像素数目
borderType要添加哪种类型的边界:默认是 cv2.BORDER_DEFAULT
cv2.BORDER_CONSTANT添加有颜色的常数值边界,还需要下一个参数(value)
cv2.BORDER_REFLIECT边界元素的镜像。例如:fedcba | abcdefgh | hgfedcb
cv2.BORDER_101或者cv2.BORDER_DEFAULT跟上面一样,但稍作改动,例如:gfedcb | abcdefgh | gfedcba
cv2.BORDER_REPLICATE复后一个元素。例如: aaaaaa| abcdefgh|hhhhhhh
cv2.BORDER_WRAP 不知怎么了, 就像样: cdefgh| abcdefgh|abcdefg

17、c++中的Mat矩阵的属性

参考
包括:data、size、depth、elemSize、step 等属性。

18、与opencv相关的辅助

(1)常用颜色的RGB值 参考1、参考2

color_list = [np.array([0, 0, 255]), np.array([0, 255, 255]), np.array([255, 255, 0]), np.array([0, 255, 0])]  # opencv是B、G、R的顺序进行存储。红色、黄色、青色、绿色。

19、旋转图片

参考1、参考2

>>> m = np.array([[1,2],[3,4]], int)
>>> np.rot90(m)  # 旋转一次90度
array([[2, 4],
       [1, 3]])
>>> np.rot90(m, 2)  # 旋转2 次90度。即 180 度。
array([[4, 3],
       [2, 1]])

20、从内存中读取图片 imdecode()

参考官方
Python: cv2.imdecode(buf, flags) → retval
Parameters:
buf – 输入待解码的数组。用imencode可以为图片编码。
flags – 与imread里面的一致,0代表灰度图,1代表RGB三通道图。

bin_str = binascii.unhexlify(hex_data)  # hex_data是十六进制字符串,改函数用来解码为二进制。
bin_array = np.fromstring(bin_str, dtype='uint8')  # 二进制转为数组的形式。
img = cv2.imdecode(buf=bin_array, flags=0)  # 解码为灰度图。

21、把图片编码后放到内存中 imencode()

参考官方

如果觉得imwrite导出不大好,就可以考虑用这个:导出成一个字符串,也能导出到磁盘。后面还可以转为其它进制(如16进制)就更节省空间。

【例子我还是没实践,不过可以做的,它与imdecode相对应。】

22、画直线 cv2.line()

参考官方、lineType参数用于改变线的产生算法
C++: void line(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)
Python: cv2.line(img, pt1, pt2, color[, thickness[, lineType[, shift]]]) → None

Parameters:	
img – 图像
pt1 – 第一个点的坐标
pt2 – 第二个点的坐标
color – 线的颜色
thickness – 线的粗细
lineType – 改变线的产生算法。使用默认就好。
	8 (or omitted) - 8联通线(默认)
	4 - 4联通线
	CV_AA - 抗锯齿线。
shift – 点坐标中的小数位数。不用管。
例子:
cv2.line(img, (x_52, y_52), (x_61, y_61), (255, 0, 0), 3)  # 使用绿色画,粗细为3.

你可能感兴趣的:(编程语言)