半吊子 opencv学习笔记
OpenCV4.1+python学习笔记
你好,我是一名大三的非计算机专业的学生,最近因为参加比赛需要实现用摄像头寻找一个圆柱体坐标并测算距离的功能。方案打算用淘宝买的双目镜头进行特征值检测,识别到物体后进行测距,具体的实现过程需要进一步的学习。我打算先在我的台式的Ubuntu上成功搞定这个功能后移植到nano上。有一定python基础,Opencv从零开始自学,希望能用大概一周的时间搞定掉这个功能。
写这篇博客记录我的学习与实践过程,同时也希望帮助有需要的人,欢迎大家在评论里指出我的错误。
部分教程参考自EX2TRON
在这里做几个跳转方便以后看这篇文章
- 环境搭建
- 读取双目镜头视频信号
- 对图像的基本操作
- 阈值分割
- 显示帧数
- 图像几何变换
- 模板匹配
- 函数库
- 遇到的问题
环境搭建
环境搭建教程
装环境时参考的这篇文章,中间遇到了一个在执行sudo apt install 时进程被占用的小问题,最开始用查找进程然后kill pid的方法,但始终有一个调用了apt的进程无法关闭,最后还是用了重启大法,重启后无卡顿每一步完美执行,最后进行测试
import cv2print(cv2.__version__)4.1.1
返回opencv版本为4.1.1,结果无误,成功配置环境
读取双目镜头视频信号与加载图片
最开始读取视频只有左摄像头有输出,以为是输入源只设置了一个,于是使用下面的指令查看了一下我的双目镜头的video编号。
$ ls /dev/video*
/dev/video0
只有一个输入,经查询得知,我用的是双目的镜头,虽然有两个镜头的输出但是只有一根usb线,只在usb总线上只有一个视频输入源。
思考了一下,想起淘宝里的商品描述写着如果要输出双目的信号必须设置特定的分辨率,于是设置了一下分辨率,分辨率改为1280*480果然输出了两路视频信号。
Attention !!一定要设置一个退出的按键,否则点×是不好使的,因为只能×掉这一帧的图片,是关不掉程序的。在程序的末尾随便设置一个退出键就ok了
if cv2.waitKey(1) == ord('q'):break
读取双目的的完整程序如下
import cv2
capture = cv2.VideoCapture(0)
# 设置摄像头分辨率
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
while(True):# 获取一帧ret,frame = capture.read(1)#转换成灰度图gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)cv2.imshow('frame',gray)if cv2.waitKey(1) == ord('q'):break
加载图片:
使用cv2.imgread()函数,该函数有两个参数:
参数1:图片的文件名
如果图片放在当前文件夹下,直接写文件名就行了,如’lena.jpg’
否则需要给出绝对路径,如’D:\OpenCVSamples\lena.jpg’
参数2:读入方式,省略即采用默认值
cv2.IMREAD_COLOR:彩色图,默认值(1)
cv2.IMREAD_GRAYSCALE:灰度图(0)
cv2.IMREAD_UNCHANGED:包含透明通道的彩色图(-1)
注意路径名中不能有中文。
对图像的基本操作
我们先读入一张图片:
import cv2
img = cv2.imread('lena.jpg')
img[y,x]对应的是这张图片在y行x列像素点的BGR值(对于彩色图),对于灰度或者是单通道的图片只有一个值。
px = img[100, 90]
print(px) # [103 98 197]
# 只获取蓝色blue通道的值
px_blue = img[100, 90, 0]
print(px_blue) # 103
修改像素的值也是同样的方式:
img[100, 90] = [255, 255, 255]
print(img[100, 90]) # [255 255 255]
注意:这步操作只是内存中的img像素点值变了,因为没有保存,所以原图并没有更改。
图片的属性
img.shape获取图像的形状,图片是彩色的话,返回一个包含行数(高度)、列数(宽度)和通道数的元组,灰度图只返回行数和列数:
print(img.shape) # (263, 247, 3)
# 形状中包括行数、列数和通道数
height, width, channels = img.shape
# img是灰度图的话:height, width = img.shape
img.dtype获取图像数据类型:
print(img.dtype) # uint8
img.size获取图像总像素数:
print(img.size) # 263*247*3=194883
ROI
Region of interest 感兴趣区域,用来节省算力,一副图片我要提取眼睛,眼睛肯定在脸上所以不是脸的区域我都不care。
提取ROI
face=img[100:200,150:160] #提取第100行到200行中150列到160列为ROI
阈值分割
固定阈值分割
非常直接,就是像素点的阈值大于定义的阈值就是一类值,小于就是一类值。大二的时候做的智能车比赛用的鹰眼ov7725就是固定阈值分割的,将灰度图处理成黑白二值化的图像,阈值可以自己修改寄存器来制定大小。
自适应阈值分割
将图像分割为多个小块,取每个小块的均值之和进行加权计算得出的阈值为整幅图像的阈值,适用于明暗分布不均的图片。使用函数为cv2.adaptiveThreshold(),具体说明见函数库。
这里使用了python的matplotlib库进行图像显示,我第一次接触这个库,打算用多少学多少,看着函数名加上百度把这些函数的功能猜的七七八八,大概弄明白是干什么的就好。
看了半天matplotlib库结果编译提示GTK版本不兼容,想要弄好好像挺费劲就没用。
固定均值
小区域内取均值
小区域内取高斯和
自动阈值完整代码
import cv2
capture = cv2.VideoCapture(0)
# 设置摄像头分辨率
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
while(True):# 获取一帧ret,frame = capture.read()#转换成灰度图gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)# 固定阈值ret,th1 = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)# 自适应阈值th2 = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 4)th3 = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 17, 6)cv2.imshow('frame',th2)if cv2.waitKey(1) == ord('q'):break
大津法(OTSU)
Otsu的教程,写的非常好
按照教程给列的式子自己推导了一下,数学太烂,非常简单的东西推了快半个小时,不过推完感觉对这个算法理解了一些。
当时做智能车的时候队友就一直吵吵要用这个算法不过当时时间比较紧张没有学,以为是特别高端的算法其实也不算太难。
大津法将图片视为背景和前景,区分背景和前景的是阈值,通过遍历所有阈值找出使这幅图像背景与前景区分最明显的一个值,反应到数学上就是方差,遍历求方差的最大值。
非常适合用在那种对比度明显的场合,比如说飞卡的赛道,用了这个算法就不用每次自己根据光照环境去手动调阈值了,大津前辈真是强!
usb线太短了拍不到飞卡赛道,拿我的ipad做下示范。
大津算法完整代码:
import cv2
import matplotlib.pyplot as plt
capture = cv2.VideoCapture(0)
# 设置摄像头分辨率
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
while(True):# 获取一帧ret,frame = capture.read()#转换成灰度图gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)# Otsuret,th1 = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)plt.subplot(3, 1, 2)plt.imshow(th1, 'gray')plt.xticks([]), plt.yticks([])plt.title('Otsu', fontsize=8)plt.subplot(3, 3, 3)plt.hist(frame.ravel(), 256)plt.xticks([]), plt.yticks([])plt.title('Histogram', fontsize=8)plt.show()if cv2.waitKey(1) == ord('q'):break
显示帧数
由于我做的是机器人比赛,在处理视频信号时帧数越高,控制周期越短,响应的更及时一些。所以显示帧数也是一个比较大的需求。
上网查了一下opencv中居然木有封装好的函数,找了别人写的c++版本,然后翻译成了python的。
具体思路就是可以获取操作系统启动以来的计时的周期和cpu的频率,每次开始获取图像时进行计时,获取到图像后将时间-开始时的时间可得获取图像所用周期,除以主频就是时间,就是fps的倒数。
完整程序如下:
import cv2
capture = cv2.VideoCapture(0)
# 设置摄像头分辨率
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
t = float (0)
while(True):# 开始计时,操作系统从启动到现在所经历的周期数t = cv2.getTickCount();# 获取一帧ret,frame = capture.read()if ret == True :#获取一帧消耗的时间=获取一帧所用的周期数/cpu的频率t = (cv2.getTickCount()-t) / cv2.getTickFrequency()#估算fps,就是获取一帧时间所用的倒数fps = round(1.0/t,2)fps = str(fps)#转换成灰度图gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)cv2.putText(gray,fps,(5,20),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,0))cv2.imshow('frame',gray)if cv2.waitKey(1) == ord('q'):break
运行效果图:
左上角是帧数,弄得有点太小了
以后有时间了把这个封装成函数,这么用太费劲了。
_(: 」∠)_
图像几何变换
缩放何旋转都比较简单就不做了,参照下边的两个函数就可以实现。
平移图像
要平移图片,我们需要定义下面这样一个矩阵,tx,ty是向x和y方向平移的距离:
M=
[
1 0 tx
0 1 ty
]
平移是用仿射变换函数cv2.warpAffine()实现的,这里边进行矩阵运算时调用了python的numpy的库需要import一下,numpy是python的一个负责进行矩阵运算的拓展库。
平移完整程序:
import cv2
import numpy as np
capture = cv2.VideoCapture(1)
# 设置摄像头分辨率
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
while(True):# 获取一帧ret,img = capture.read(1)rows,cols = img.shape[:2]#定义平移矩阵M = np.float32([[1,0,100],[0,1,50]])#用放射变换实现平移dst = cv2.warpAffine(img,M,(cols,rows))#转换成灰度图gray = cv2.cvtColor(dst,cv2.COLOR_BGR2GRAY)cv2.imshow('frame',gray)if cv2.waitKey(1) == ord('q'):break
旋转图像
也是调用函数就可以实现,cv2.getRotationMatrix2D(),具体函数说明见函数库。
模板匹配
模板匹配就是大图中找下图,适应性不好,在图像大小和角度改变的情况下匹配度比较低,不适合用做物体识别。
模板匹配完整代码:
import numpy as np
import cv2
capture = cv2.VideoCapture(0)
# 设置摄像头分辨率
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
template1 = cv2.imread('3.png',0)
h,w = template1.shape[:2]
threshold = 0.95
while(True):# 获取一帧ret,img = capture.read(1)#转换成灰度图gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)res = cv2.matchTemplate(gray, template1, cv2.TM_CCOEFF_NORMED)min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)print(max_val)left_top = max_loc # 左上角right_bottom = (left_top[0] + w, left_top[1] + h) # 右下角cv2.rectangle(gray, left_top, right_bottom, 255, 2) # 画出矩形位置cv2.imshow('frame',gray)if cv2.waitKey(1) == ord('q'):break
函数库
cv2.VideoCapture()
要使用摄像头,需要使用cv2.VideoCapture(0)创建VideoCapture对象,参数0指的是摄像头的编号,如果你电脑上有两个摄像头的话,访问第2个摄像头就可以传入1,依此类推.
cv2.capture.read()
capture.read()函数返回的第1个参数ret(return value缩写)是一个布尔值,表示当前这一帧是否获取正确,第二个就是当前这一帧的图像了。
cv2.cvtColor()
cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.cvtColor()用来转换颜色,这里将彩色图转成灰度图。
cv2.waitKey()
if cv2.waitKey(1) == ord('q')://检测按键q是否被按下
检测按键是否被按下,十分友好,炒鸡好用!!
cv2.imgread()
参数1:图片的文件名
如果图片放在当前文件夹下,直接写文件名就行了,如’lena.jpg’
否则需要给出绝对路径,如’D:\OpenCVSamples\lena.jpg’
参数2:读入方式,省略即采用默认值
cv2.IMREAD_COLOR:彩色图,默认值(1)
cv2.IMREAD_GRAYSCALE:灰度图(0)
cv2.IMREAD_UNCHANGED:包含透明通道的彩色图(-1)
import cv2
# 加载灰度图
img = cv2.imread('lena.jpg', 0)
cv2.threshold()
threshold是阈值的意思
用来实现阈值分割,ret是return value缩写,代表当前的阈值,暂时不用理会。函数有4个参数:
参数1:要处理的原图,一般是灰度图
参数2:设定的阈值
参数3:最大阈值,一般为255
参数4:阈值的方式,主要有5种
五种阈值的方式如下:
第一种THRESH_BIANARY:意思就是大于你设定的阈值就是maxval(一般都是255),小于你设定的阈值就是0。
第二种THRESH_BIANARY_INV:后边那个inv是invertrd 翻转的意思就是大于设定阈值的变成0小于的变成maxval。
第三种THRESH_TRUNC: trunc的意思是取整,就是大于阈值的都变成阈值,其他的都变成maxval。
第四种THTRSH_TOZERO:大于阈值的不变,其他的都取0.
第五种:大于阈值的变成0,其他的不变。
应用5种不同的阈值方法
ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
ret, th2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
ret, th3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
ret, th4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
ret, th5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
cv2.adaptiveThreshold()
看得出来固定阈值是在整幅图片上应用一个阈值进行分割,它并不适用于明暗分布不均的图片。 cv2.adaptiveThreshold()自适应阈值会每次取图片的一小部分计算阈值,这样图片不同区域的阈值就不尽相同。它有6个参数
参数1:要处理的原图
参数2:最大阈值,一般为255
参数3:小区域阈值的计算方式
ADAPTIVE_THRESH_MEAN_C:小区域内取均值
ADAPTIVE_THRESH_GAUSSIAN_C:小区域内加权求和,权重是个高斯核
参数4:阈值方式(跟前面讲的那5种相同)
参数5:小区域的面积,如11就是11*11的小块
参数6:最终阈值等于小区域计算出的阈值再减去此值
我理解这个函数的意思是通过对整幅图像进行分割,然后为每个小方块进行加权求和最后得出的阈值相对于手动设置的阈值效果更好一点。
cv2.resize()
缩放图片
# 按照指定的宽度、高度缩放图片
res = cv2.resize(img, (132, 150))
# 按照比例缩放,如x,y轴均放大一倍
res2 = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR
cv2.flip()
镜像翻转图片。
dst = cv2.flip(img, 1)
其中,参数2 = 0:垂直翻转(沿x轴),参数2 > 0: 水平翻转(沿y轴),参数2 < 0: 水平垂直翻转。
cv2.getRotationMatrix2D()
旋转同平移一样,也是用仿射变换实现的,因此也需要定义一个变换矩阵。OpenCV直接提供了 cv2.getRotationMatrix2D()函数来生成这个矩阵,该函数有三个参数:
参数1:图片的旋转中心
参数2:旋转角度(正:逆时针,负:顺时针)
参数3:缩放比例,0.5表示缩小一半
45°旋转图片并缩小一半
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 45, 0.5)
dst = cv2.warpAffine(img, M, (cols, rows))
cv2.imshow('rotation', dst)
cv2.waitKey(0)
遇到的问题
1 安装环境
在我用笔记本装opencv的环境时,使用同样的步骤,每一步都没有问题,到最后验证版本时提示无法找到cv2模块,重试了几次都有这个问题。目前没找到解决办法,如果笔记本的ubuntu装不上的话,可能会考虑在Windows下装一下环境。
问题解决了 使用
sudo apt install python3-opencv
2 GTK版本不兼容
在使用Matplotlib库时提示过这个错误,一开始以为要重新安装OpenCV后来发现调用的时候使用这个就没问题了。
import matplotlib.pyplot as plt
Attention好像不是这么一回事,调用matplotlib库的时候不应使用显示视频的imshow,否则就会报版本不兼容的错误
半吊子 opencv学习笔记相关推荐
- OpenCV 学习笔记03 boundingRect、minAreaRect、minEnclosingCircle、boxPoints、int0、circle、rectangle函数的用法...
函数中的代码是部分代码,详细代码在最后 1 cv2.boundingRect 作用:矩形边框(boundingRect),用于计算图像一系列点的外部矩形边界. cv2.boundingRect(arr ...
- opencv学习笔记(二):基于肤色的人手检测
opencv学习笔记(二):基于肤色的人手检测 原文:http://blog.csdn.net/wzmsltw/article/details/50849810 先写了人手的检测程序,下一步基于检测程 ...
- python做直方图-python OpenCV学习笔记实现二维直方图
本文介绍了python OpenCV学习笔记实现二维直方图,分享给大家,具体如下: 官方文档 – https://docs.opencv.org/3.4.0/dd/d0d/tutorial_py_2d ...
- OpenCV学习笔记大集锦
转载自: OpenCV学习笔记大集锦 – 视觉机器人 http://www.cvrobot.net/collect-opencv-resource-learn-study-note-chinese/ ...
- OpenCV学习笔记(五十六)——InputArray和OutputArray的那些事core OpenCV学习笔记(五十七)——在同一窗口显示多幅图片 OpenCV学习笔记(五十八)——读《Mast
OpenCV学习笔记(五十六)--InputArray和OutputArray的那些事core 看过OpenCV源代码的朋友,肯定都知道很多函数的接口都是InputArray或者OutputArray ...
- OpenCV学习笔记(五十一)——imge stitching图像拼接stitching OpenCV学习笔记(五十二)——号外:OpenCV 2.4.1 又出来了。。。。。 OpenCV学习笔记(五
OpenCV学习笔记(五十一)--imge stitching图像拼接stitching stitching是OpenCV2.4.0一个新模块,功能是实现图像拼接,所有的相关函数都被封装在Stitch ...
- OpenCV学习笔记(四十六)——FAST特征点检测features2D OpenCV学习笔记(四十七)——VideoWriter生成视频流highgui OpenCV学习笔记(四十八)——PCA算
OpenCV学习笔记(四十六)--FAST特征点检测features2D 特征点检测和匹配是计算机视觉中一个很有用的技术.在物体检测,视觉跟踪,三维常年关键等领域都有很广泛的应用.这一次先介绍特征点检 ...
- OpenCV学习笔记(四十一)——再看基础数据结构core OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年 OpenCV学习笔记(四十三)——存取像素值操作汇总co
OpenCV学习笔记(四十一)--再看基础数据结构core 记得我在OpenCV学习笔记(四)--新版本的数据结构core里面讲过新版本的数据结构了,可是我再看这部分的时候,我发现我当时实在是看得太马 ...
- OpenCV学习笔记(三十六)——Kalman滤波做运动目标跟踪 OpenCV学习笔记(三十七)——实用函数、系统函数、宏core OpenCV学习笔记(三十八)——显示当前FPS OpenC
OpenCV学习笔记(三十六)--Kalman滤波做运动目标跟踪 kalman滤波大家都很熟悉,其基本思想就是先不考虑输入信号和观测噪声的影响,得到状态变量和输出信号的估计值,再用输出信号的估计误差加 ...
最新文章
- C# 多网卡 Server Listen
- python2 去除 字符串中emoji 符号,去除所有4字节utf8字符
- 杭电oj1087最长递增子序列java实现
- 山科大计算机专业排名,山东科技大学专业排名情况
- 项目问题记录20170702
- 编程式事务控制相关对象
- jetty启动源码分析
- mysql 存储过程游标删除_mysql数据库存储过程游标循环,提前退出
- [蓝桥杯][算法提高VIP]学霸的迷宫(bfs+dfs)
- mac mongodb 安装
- 汇编级UART串口初始化与打印
- informix利用dbaccess倒出数据
- java实现点卡生成
- 锐捷校园网:小米mini路由刷Padavan固件实现wifi上网
- Collaborative Evolutionary Reinforcement Learning
- python runner.daemonrunner_python3.3.4:pythondaemon3K;如何使用runn
- python给全局变量赋值_Python 进程之间共享数据(全局变量)的方法 python 全局变量赋值的问题...
- route和bridge是什么意思_请问ROUTE 和 BRIDGE 是怎么分别的!
- 中职高二学生计算机学情分析,高二学生学习数学的学情分析.doc
- eclips 换工作区间
热门文章
- mysql foreign key references_使用带有和不带有FOREIGN KEY的REFERENCES之间的区别?
- validates resource references inside Android XML files
- 用户说明书,还是用户操作手册?
- Django中间件——实现登陆验证
- linux windows 垃圾清理,windows垃圾清理
- 移动web开发meta标签的使用
- 25套高级简约 开题报告 毕业答辩 项目汇报 科研成功展示 医疗PPT模板
- js导出EXCEL js导出EXCEL
- html 下拉框a标签跳转,html下拉框跳转问题
- C++_32位和64程序