在这一部分我们将介绍在OpenCV中的GUI特性之图片和视频的读写和显示,包括图像的cv.imread / imshow / imwrite和VideoCapture/Writer等函数的使用
目录
在OpenCV中的基础图片操作涉及了三个函数:cv.imread(),cv.imshow(),cv.imwrite()
使用imread()函数并传入两个参数,第一个参数是图片的路径(在项目目录下的相对路径或在计算机上的绝对路径),第二个参数是一个标志,它指定了图像的读取方式:彩色图像、灰度图或包括alpha通道读取图像
第二个参数即是一个表示,它包含了三个相关的参数:
这三个图像可以用三个简单的参数代替:1/0或-1,分别代表上述的三个标志
显示图片使用imshow()函数可以实现,其同样需要传递两个参数,第一个参数代表窗口的名称,它是一个字符串,第二个参数代表图像对象
需要注意的是,通过imshow()函数显示图片时,窗口大小自适应图片的尺寸,当然我们也可以人为地改变或规定窗口的尺寸
代码实现
import numpy as np
import cv2 as cv
#加载彩色灰度图像
img = cv.imread('messi5.jpg',0)
cv.imshow('image',img)
cv.waitKey(0)
cv.destroyAllWindows()
通过上面的代码我们观察发现,在使用imread()函数读取图像并用imshow()函数显示图片之后,我们又使用了两个函数:cv.waitKey()和cv.destoryAllWindows()
cv.destroyAllWindows()只会破坏我们创建的所有窗口,如果要销毁任何特定的窗口,请使用函数cv.destroyWindow()在其中传递确切的窗口名称作为参数
数 cv.destroyWindow()在其中传递确切的窗口名称作为参数
cv.waitKey()函数是一个键盘绑定函数,其参数是以毫秒为单位的时间,该函数等待任意键盘事件指定的毫秒,如果你在这段时间内按下任何键,程序将继续运行,如果0被传递,它将无限期地等待一次敲击键,它也可以设置为检测特定的按键
我们举一个按下键盘 ‘q’ 键后关闭窗口的例子
cv.imshow('frame', img)
if cv.waitKey(1) == ord('q'):
break
cv.destroyAllWindows()
这里我们使用了ord()函数作为捕获键盘事件的一个方式,ord()的参数是一个字符,它会返回这个字符对应的ASCII 数值,或者 Unicode 数值(取决于编码方式),然后询问cv.waitKey()函数的返回值是否等于ord()函数的返回值,而在这里,ord()函数的返回值就是键盘键 ‘q’ 的ASCII数值
我们再举一个按下键盘Esc键关闭窗口的例子
cv2.imshow('res', res)
if cv2.waitKey(1) == 27:
break
# 关闭窗口
cv2.destroyAllWindows()
这里我们没有使用ord()函数,而是直接询问cv.waitKey()函数的返回值是否等于某个数,而这里的27这个数,就代表着键盘的Esc键
关于图片的写入关乎到函数cv.imwrite()保存图像,其包含了两个参数,第一个参数是文件名,第二个参数是我们想保存的图像对象
比如语句cv.imwrite(‘messigray.png’,img),这会将图像img以PNG格式保存在工作目录中
总结代码实现
import numpy as np
import cv2 as cv
img = cv.imread('messi5.jpg',0)
cv.imshow('image',img) k = cv.waitKey(0)
if k == 27: # 等待ESC退出
cv.destroyAllWindows()
elif k == ord('s'): # 等待关键字,保存和退出
cv.imwrite('messigray.png',img)
cv.destroyAllWindows()
Matplotlib是Python的绘图库,可以为你提供多种绘图方法,在这一小节我们先学习如何使用Matplotlib显示图像以及拓展学习常用的plt.subplot()函数的功能和用法
关于使用Matplotlib绘图库绘图的代码实现:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg',0)
plt.imshow(img, cmap = 'gray', interpolation = 'bicubic')
plt.xticks([]), plt.yticks([]) # 隐藏 x 轴和 y 轴上的刻度值
plt.show()
在这里我们需要拓展学习的是plt的xticks()函数、yticks()函数和·imshow()函数
1、xticks()和yticks()这两个函数都有三个参数:locs, [labels], **kwargs
“ * ” 表示该位置接受任意多个非关键字参数,在函数中将其转化为元组
“ ** ” 表示这个位置接受任意多个关键字参数,在函数中将其转化为字典
第一个参数locs表示x轴或y轴刻度线标注的位置,加入我们想在刻度线上表示1-12的所有数字,我们可以把locs参数设置为range(1,13,1)来满足我们的需求
第二个参数[labels]表示横纵坐标上各刻度的名称,可以是月数、人们等,它是一个数组类型的参数
第三个参数**kwargs提供了两个关键字供我们使用:color和rotation,color可以赋予其字符串类型的值,它可以设置刻度标签的显示颜色,而rotation可以赋予其整型或浮点型的数字,它代表了刻度标签按逆时针旋转的角度,综合使用这些属性可以让我们绘出的图形更加美观
2、imshow()函数有些复杂,在这里我们不多做解释,仅解释代码中出现的三个参数即可:img、camp和interpolation
img参数表示图像数据,camp参数将标量数据映射到色彩图,其取值默认为img.cmap,但是在代码中我们将其设置为"gary"表示将原来的色彩图以灰度图的形式映射,interpolation表示使用的插值方式,关于插值方式我们在后面的文章再细说
关于OpenCV中视频的读写和展示我们着重学习两个功能:cv.VideoCapture(),cv.VideoWriter()
在视频的读取中,我们首先要创建一个VideoCapture对象用来捕获视频,在这个功能中我们需要传入一个参数,这个参数是设备索引或文件名称,设备索引在当前阶段我们可以简单地认为“ 0 ”就是我们笔记本电脑自带的摄像头的设备索引,“ 1 ”就是我们附加的一个外设摄像头的设备索引,以此类推
在此之后我们要逐帧捕获视频(在最后不用忘记释放俘虏!),上代码!
import numpy as np
import cv2 as cv
cap = cv.VideoCapture(0)
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
# 逐帧捕获
ret, frame = cap.read()
# 如果正确读取帧,ret为True
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
break
# 我们在框架上的操作到这里
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# 显示结果帧e
cv.imshow('frame', gray)
if cv.waitKey(1) == ord('q'):
break
# 完成所有操作后,释放捕获器
cap.release()
cv.destroyAllWindows()
代码解释:如我们前面所说的一样,在读取视频前我们先创建一个VideoCapture对象用来捕获视频(代码中我们传入的参数是“ 0 ”,所以此时我们用的就是笔记本自带的摄像头或台式计算机外接的第一个外设摄像头!),然后万年不变的是要先判断视频捕捉器(摄像头)的状态,是的就是cap.isOpened()函数,摄像头的状态是否正常,就反映在这个函数的返回值上
在后来就要进入我们的核心代码区了:逐帧捕获
我们使用了一个while循环来重复执行捕捉帧的操作,ret和frame这两个一个都不能少,我们用read()读取(当然后面有用下划线“ _ ”代替的情况,我们到时候再说),逐帧捕捉后,为了处理一些异常或者保持我们程序的安全性,要判断此帧有没有问题,如果有问题就要作出相应的处理:在控制台打印错误提示然后退出视频捕捉器,如果没有问题就继续往下进行,我们把这个一小块图像frame用cvtColor(frame, cv.COLOR_BGR2GRAY)函数改变为灰度图,然后用gray存储frame,并通过imshow()函数显示这一小块图像,往复进行,就实现了我们的视频读取
我们还可以使用 cap.get(propId) 方法访问该视频的某些功能,其中propId是0到18之间的一个数字,个数字表示视频的属性(如果适用于该视频),并且可以显示完整的详细信息在这里看到:cv.VideoCapture::get()。其中一些值可以使用 cap.set(propId,value) 进行修改,value是你想要的新值
例如,我可以通过 cap.get(cv.CAP_PROP_FRAME_WIDTH) 和 cap.get(cv.CAP_PROP_FRAME_HEIGHT)检查框架的宽度和高度
# 改变框架的分辨率
ret = cap.set(cv.CAP_PROP_FRAME_WIDTH,320)
ret = cap.set(cv.CAP_PROP_FRAME_HEIGHT,240)
它与从相机捕获相同,只是用视频文件名更改摄像机索引,在显示框架时要特别注意cv.waitKey()函数的参数传递,如果参数太小则视频加速播放,如果参数太大则视频慢速播放(这就是快/动作放映的方式,正常情况下25毫秒即可)
代码实现
import numpy as np
import cv2 as cv
cap = cv.VideoCapture('vtest.avi')
while cap.isOpened():
ret, frame = cap.read()
# 如果正确读取帧,ret为True
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
break
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
cv.imshow('frame', gray)
if cv.waitKey(1) == ord('q'):
break
cap.release()
cv.destroyAllWindows()
还记得我们保存图像时用的imwrite()函数吗,在保存图像时我们要换一种新的方式,那就是使用VideoWriter对象
在保存视频时,我们先要创建一个VideoWriter对象,然后指定输出的文件名,指定FourCC代码,传递帧率的数量和大小,最后标志颜色
在Fedora中:DIVX,XVID,MJPG,X264,WMV1,WMV2(最好使用XVID,MJPG会生成大尺寸的视频。X264会生成非常小的尺寸的视频)
在Windows中:DIVX(尚待测试和添加)
在OSX中:MJPG(.mp4),DIVX(.avi),X264(.mkv)
FourCC代码作为MJPG的 cv.VideoWriter_fourcc(‘M’,‘J’,‘P’,‘G’) or cv.VideoWriter_fourcc(*‘MJPG’) 传递
代码实现:从摄像头捕获的画面沿垂直方向翻转并保存
import numpy as np
import cv2 as cv
cap = cv.VideoCapture(0)
# 定义编解码器并创建VideoWriter对象
fourcc = cv.VideoWriter_fourcc(*'XVID')
out = cv.VideoWriter('output.avi', fourcc, 20.0, (640, 480))
while cap.isOpened():
ret, frame = cap.read()
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
break
frame = cv.flip(frame, 0)
# 写翻转的框架
out.write(frame)
cv.imshow('frame', frame)
if cv.waitKey(1) == ord('q'):
break
# 完成工作后释放所有内容
cap.release()
out.release()
cv.destroyAllWindows()
(注:文章内容参考OpenCV4.1中文官方文档)
如果文章对您有所帮助,记得一键三连支持一下哦~
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢