当前位置: 首页 > article >正文

【2】图像视频的加载和显示

文章目录

  • 【2】图像视频的加载和显示
  • 一、代码在哪写
  • 二、创建和显示窗口
    • (一)导入OpenCV的包`cv2`
    • (二)创建窗口
    • (三)更改窗口大小 & 显示窗口
    • (四)等待用户输入
        • 补充:ord()函数来返回ASCII值
    • (五)销毁窗口
    • (※)完整代码
  • 二、加载显示图片
    • (一)cv2.imread()返回值
    • (二)用matplotlib显示图片
    • (三)用OpenCV显示图片
      • (1)封装函数
      • (2)封装外部.py文件
    • (四)保存图片
  • 三、视频采集和录制
    • (一)视频采集
    • (二)视频录制
  • 四、控制鼠标
  • 五、TrackBar控件

【2】图像视频的加载和显示

一、代码在哪写

注:上一节中已经创建好了虚拟环境。

打开Anaconda Prompt,切换到我们已经创建好的opencv虚拟环境下。

image-20240910205759402

我们使用jupyter notebook来写代码。(输入jupyter notebook

image-20240910205949797

之后,就弹出一个控制台页面。

image-20240910210017744

弹出的这些东西就不太对了。

为什么会弹出这一堆东西,原因在于:你在哪个目录下执行的jupyter notebook命令,它就会以哪个目录作为根目录。

image-20240910210132157

可见,此时这一堆东西都是我的C:\Users\11202目录下的文件,这些文件肯定都是不能动的,我们也不应该以这里作为我们写代码的目录

也就是说,上述是一个错误的演示。

正确的应该怎么做呢?如下。

如果是继续刚才的命令往下写,那么需要按两下ctrl+C把刚才打开的jupyter notebook先退出。

如果是关掉,又新打开的Anaconda Prompt,则不要忘了使用activate opencv先跳转到需要的虚拟环境下

然后我们使用cd命令,打开到我们想要作为根目录的路径(我的是D:\ANACONDA\dirs\test2409),然后再使用命令jupyter notebook

image-20240910212613163

注意:从C盘跳转到D盘时,不要写成cd D:,而是直接输入D:。之后,跳转到D盘后,再使用cd去打开对应文件夹。

这时,再跳转到的控制页面就是我们想要的了。之后我们可以在里面新建一个notebook,就可以写代码了。

image-20240910222157917

image-20240910222233409

二、创建和显示窗口

  • namedWindow() 创建命名窗口
  • imshow() 显示窗口
  • destroyAllwindows() 摧毁窗口
  • resizeWindow() 改变窗口大小
  • waitKey() 等待用户输入

我们依次试一下其效果。

(一)导入OpenCV的包cv2

注意:OpenCV名字虽然叫OpenCV,但是在导包的时候,导入的包叫作cv2。(这是个历史遗留问题,很早的时候它就叫cv,之后进行了重构,于是又叫cv2了,并沿用至今)

image-20240910222645716

运行没有报错,说明正确导入此包。说明我们之前的环境配置、包的安装等操作都是顺利的。

(二)创建窗口

image-20240910223021704

注意:光标放在函数上,按shift+tab可以显示对该函数用法的说明(按一下是简洁版说明,按两下是详细版说明)。这个功能也是使用jupyter notebook所带来的一个好处。

注意:如果发现按shift+tab无效,则首先检查一下是否执行过import cv2代码,因为有时候你刚打开这个文件直接就去shift+tab,发现显示不出说明。实际上是因为没有导入cv2的包,导致它不认识这个函数,自然也就不会给出说明。

这个函数的用法是,括号的内的第一个参数,是窗口名字(注意,应当是一个字符串)。后面的参数是窗口标记(可以从函数用法说明中阅读到具体介绍)。

image-20240910224639146

可见,不同的窗口标记,可以使窗口以不同的风格进行显示;以及如果不注明窗口标记,则会有一个默认值。

注意:如果想要设置flag这一参数,仅仅输入对应标记名是不够的,还要在前面加上cv2.,如cv2.WINDOW_AUTOSIZE。(注意标记名不要写错)

image-20240910225901433

运行后,的确会出现一个窗口,说明代码没问题,但是是未响应状态,这是因为我们写的不是一个规范的写法。

注意:我们把import cv2创建窗口代码写在两个不同的代码框中,此时,务必要保证先运行了import cv2之后,再去运行创建窗口代码,否则会报错(报错原因是找不到cv2这个包)。或者你就把所有代码都写在同一个框里。

(三)更改窗口大小 & 显示窗口

cv2.resizeWindow()的第一个参数为要修改大小的窗口名,后两个参数为窗口大小尺寸

cv2.imshow()的第一个参数为要显示的窗口名,第二个参数为要显示的图片。此处我们没有什么要显示的图片,因此第二个参数设置为0。

此外,需要注意的是,对于我们创建窗口时设置的窗口标记,若设置的是cv2.WINDOW_AUTOSIZE(根据内容自动调节窗口尺寸),那么你修改窗口大小是没有效果的。

注意,注释单行代码的快捷键:ctrl+/

image-20240910231632608

(四)等待用户输入

cv2.waitKey()的作用是,等待接收用户按键,并返回该按键对应的ASCII码值。

其中参数设置为0,表示一直等待、接收任意按键。如果设置其他的整数,表示等待按键的时间(单位是毫秒)。比如设置为cv2.waitKey(5000),就表示它会等你5秒,在5秒之内你按键才有用,过了5秒就不等你了。

image-20240910232204977

注意,此时观察左侧,是In [*],是星号说明这段代码正在运行中,还没有运行结束。

实际上,cv2.waitKey(0)的作用就是等待用户按一个键。当然,焦点要在窗口上时才可以。

我按了一个键盘上的q键,它就会捕捉到并输出一个113113就是q的ASCII码值。(注意是小写q,不是大写Q)

image-20240910232058134

补充:ord()函数来返回ASCII值

image-20240910232736048

(五)销毁窗口

我们可以配合cv2.waitKey(0),先接收到用户按键的ASCII码值,然后就能根据用户按的是什么键,判断是否要销毁窗口了。(常用的比如q键、ESC键)。

image-20240910233703301

按下q键后,窗口直接被销毁(关闭),而不会再有窗口未响应的问题。

但是由于程序只接收一次按键,如果你按的不是q键,它还是会有窗口未响应的问题。

image-20240910233819979

注意,如果对于key == 'q'为什么有问题有疑问,则需要补一补python基础。

(※)完整代码

# ------------------------- 创建和显示窗口 -------------------------

# opencv名字叫opencv,但是导包的时候叫做cv2
import cv2

# 创建窗口
# 注意,cv2.WINDOW_AUTOSIZE则后续修改窗口大小无效
# cv2.namedWindow('myWindow01', cv2.WINDOW_AUTOSIZE)
cv2.namedWindow('myWindow01', cv2.WINDOW_NORMAL)

# 更改窗口大小
cv2.resizeWindow('myWindow01', 800, 600)

# 显示指定名字的窗口
cv2.imshow('myWindow01', 0)

# 等待按键
# 若用户按键为q键,则销毁窗口
key = cv2.waitKey(0)  # 先用变量key接收
# 注意,这样写更易读,而不要写key == 113,即使你知道q是113
# 注意,不要写成key == 'q'
if key == ord('q'):
    print('准备销毁窗口')
    cv2.destroyAllWindows()
    
# ------------------------- 创建和显示窗口 -------------------------

二、加载显示图片

  • imread(path, flag)

使用imread可以读取图片,默认读取的是彩色图片。

path是图片的路径(绝对路径、相对路径都可以)。

flag是以什么方式读取这个图片(比如读出来是黑白的)。

(一)cv2.imread()返回值

# 导入opencv包
import cv2

# 读取图片
cat = cv2.imread('./1.png')

# numpy的ndarray(多维数组)
cat

输出如下:

array([[[  1,  12,  90],
        [  2,  12,  91],
        [  1,  13,  91],
        ...,
        [  3,  10,  61],
        [  4,   9,  61],
        [  4,   9,  65]],

       [[  3,  13,  91],
        [  3,  13,  92],
        [  2,  14,  92],
        ...,
        [  2,  11,  60],
        [  2,  10,  60],
        [  2,   9,  63]],

       [[  2,  12,  91],
        [  2,  13,  91],
        [  2,  14,  92],
        ...,
        [  3,  10,  60],
        [  3,  10,  60],
        [  3,  10,  62]],

       ...,

       [[  4,  31, 111],
        [  4,  32, 112],
        [  4,  31, 113],
        ...,
        [119, 190, 208],
        [119, 190, 208],
        [119, 190, 208]],

       [[  5,  31, 111],
        [  5,  31, 111],
        [  5,  31, 112],
        ...,
        [119, 190, 208],
        [119, 190, 208],
        [119, 190, 208]],

       [[  6,  32, 111],
        [  6,  31, 111],
        [  6,  31, 111],
        ...,
        [118, 189, 207],
        [119, 190, 208],
        [119, 190, 208]]], dtype=uint8)

(二)用matplotlib显示图片

import matplotlib.pyplot as plt

plt.imshow(cat)

我的原图是:

image-20240911104705365

输出结果如下:

image-20240911104637344

发现这个猫的样子没变,但是颜色不太对。这是因为OpenCV读取的图片颜色通道是按照BGR(蓝绿红)排列的,一般的图片通道都是按照RGB来排列的。

为了正常显示图片,我们要使用OpenCV的图像显示方法。换句话说,用OpenCV读进来的图片一般不要用别的方式进行展示,比如matplotlib。

(三)用OpenCV显示图片

cv2.imshow('cat', cat)

# 按键以销毁窗口,避免每次都有窗口未响应的问题
key = cv2.waitKey(0)
if key == ord('q'):
    print('准备销毁窗口')
    cv2.destroyAllWindows()

这样就会弹出一个窗口,并正常显示该图片,我们可以按下q键以正常关闭窗口。

(1)封装函数

此外,我们可以把显示图片的方法封装成一个函数,方便我们显示图片:

# 把展示图片的代码封装成函数
def cv_show(name, img):
    cv2.imshow(name, img)
    key = cv2.waitKey(0)
    if key == ord('q'):
        cv2.destroyAllWindows()

之后想显示图片的时候,直接调用函数即可:

# 导入opencv包
import cv2

# 读取图片
cat = cv2.imread('./1.png')

cv_show('cat', cat)

(2)封装外部.py文件

我们可以在当前目录下创建一个.py文件,把封装的函数代码放进去。

image-20240911110226843

image-20240911110256307

注意,如果这样放代码,那么在调用的时候会报错。因为,在utils.py这个文件中,我们调用了cv2,但是并没有导入cv2,就会报错。

image-20240911111714180

需要在函数中调用cv2之前,先import cv2

image-20240911110636568

这样就可以正常运行了。

image-20240911111813648

注意:如果还是有问题,可以使用%run utils.py执行一下外部文件(这是在jupyter中执行外部文件的方法),看看外部文件本身是否运行不了。如果在执行外部文件时报错IndentationError: unindent does not match any outer indentation level,则是代码缩进问题,一般是tab和空格混用导致的缩进问题,这个问题仅凭肉眼是不容易看出来的(新手容易踩的坑)。(如果使用的是notepad++,可以通过View--Show Symbol--Show All Characters来检查这一问题)

image-20240911112039540

(四)保存图片

  • imwrite(path, img):使用imwrite保存图片。
import cv2

cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.resizeWindow('img', 320, 240)

img = cv2.imread('./1.png')

# 利用while循环优化退出逻辑
while True:
    cv2.imshow('img', img)
    key = cv2.waitKey(0)
    if(key & 0xFF == ord('q')):
        break
    elif(key & 0xFF == ord('s')):
        cv2.imwrite('./123.png', img)
    else:
        print(key)
        
cv2.destroyAllWindows()

三、视频采集和录制

(一)视频采集

  • 视频是由图片组成的,视频的每一帧就是一幅图片,一般是30帧,表示一秒显示30张图片。
  • cv2.VideoCapture可以捕获摄像头,用数字来表示不同的设备,比如0、1。
  • 如果是视频文件,可以直接指定路径即可。
# 打开视频文件
vc = cv2.VideoCapture('./1.mp4')

# 打开摄像头
vc = cv2.VideoCapture(0)

打开摄像头

  示例:

# 打开摄像头
import cv2

cv2.namedWindow('video', cv2.WINDOW_NORMAL)
cv2.resizeWindow('video', 640, 480)

# 如果打开失败(比如没有摄像头),不会报错
# cap = cv2.VideoCapture(1) # 我只有摄像头0,没有摄像头1
cap = cv2.VideoCapture(0)

# 循环读取摄像头的每一帧
# while True:
while cap.isOpened():
    # 读一帧数据,返回标记和这一帧数据,标记为True表示读到了数据,False表示没读到数据
    ret, frame = cap.read()
    
    # 可以根据ret做个判断
    if not ret:
        # 没读到数据,直接退出
        break
        
    # 显示数据
    cv2.imshow('video', frame)
    
    key = cv2.waitKey(1)
    # 注意,此处就不要再写0了,因为写0表示无限等待,也就是显示一帧数据然后一直等待用户按键
    # 写个1,表示等1毫秒,若等不到按键就继续处理了
    
    if key == ord('q'):
        break
        
# 别忘了释放资源
cap.release()
cv2.destroyAllWindows()

打开视频文件

  示例:

# 打开摄像头
import cv2

cv2.namedWindow('video', cv2.WINDOW_NORMAL)
cv2.resizeWindow('video', 640, 480)

# 打开视频,传入视频路径即可
cap = cv2.VideoCapture('./1.mp4')

# 循环读取摄像头的每一帧
# while True:
while cap.isOpened():
    # 读一帧数据,返回标记和这一帧数据,标记为True表示读到了数据,False表示没读到数据
    ret, frame = cap.read()
    
    # 可以根据ret做个判断
    if not ret:
        # 没读到数据,直接退出
        break
        
    # 显示数据
    cv2.imshow('video', frame)
    
    key = cv2.waitKey(1)
    # 注意,此处就不要再写0了,因为写0表示无限等待,也就是显示一帧数据然后一直等待用户按键
    # 写个1,表示等1毫秒,若等不到按键就继续处理了
    
    # key = cv2.waitKey(1000 // 30)  # 让视频以30帧播放
    
    if key == ord('q'):
        break
        
# 别忘了释放资源
cap.release()
cv2.destroyAllWindows()

  注意,上述代码,和刚才“打开摄像头”的代码只有一行不同,即第8行的cap = cv2.VideoCapture('./1.mp4'),其他地方都相同。

  运行此代码时,发现其播放的视频好像加速了一样,这是因为,第24行代码的key = cv2.waitKey(1)造成的效果是每等待1毫秒显示一帧数据,所以播放的比较快。

  假如我们想让视频是30帧,那么每张图片要间隔多少毫秒?

  答:理论上来说,是1000 / 30ms,但此处传递的参数必须是整数,所以我们代码写成1000 // 30。(Python语法,//表示向下取整)

  另外,我们此处处理的是视频中每一帧的图片,所以没有声音。

(二)视频录制

  • VideoWriter:参数1为输出文件,参数2为多媒体文件格式(VideoWriter_fourcc),参数3为帧率,参数4为分辨率;
  • write编码并写入缓存;
  • release缓存内容写入磁盘,并释放资源。
import cv2

cap = cv2.VideoCapture(0)

# *mp4v 就是解包操作,等同于 'm','p','4','v'
# fourcc = cv2.VideoWriter_fourcc(*'mp4v')

# avi格式的视频
fourcc = cv2.VideoWriter_fourcc(*'XVID')

# (640, 480)表示摄像头拍视频的分辨率,这个大小搞错了也不行
# vw = cv2.VideoWriter('output.mp4', fourcc, 20, (640, 480))

# 如果前面写的是avi,这里要改成avi格式
vw = cv2.VideoWriter('output.avi', fourcc, 20, (640, 480))

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        print('can not receive frame, Exiting...')
        break
        
    vw.write(frame)
    cv2.imshow('frame', frame)
    
    if cv2.waitKey(1) == ord('q'):
        break
        
cap.release()

vw.release()

cv2.destroyAllWindows()

四、控制鼠标

  OpenCV允许我们对窗口上的鼠标动作做出响应。

  • setMouseCallback(winname, callback, userdata):winname是窗口名字,callback是回调函数,userdata是给回调函数的参数。
  • callback(event, x, y, flags, userdata):回调函数必须包含这5个参数。event是事件(鼠标移动、左键、右键),xy是点鼠标的坐标点,flags主要用于组合键,userdata就是上面的setMouseCallback的userdata。
鼠标事件:
EVENT_MOUSEMOVE 0 鼠标移动
EVENT_LBUTTONDOWN 1 按下鼠标左键
EVENT_RBUTTONDOWN 2 按下鼠标右键
EVENT_MBUTTONDOWN 3 按下鼠标中键
EVENT_LBUTTONUP 4 左键释放
EVENT_RBUTTONUP 5 右键释放
EVENT_MBUTTONUP 6 中键释放
EVENT_LBUTTONDBLCLK 7 左键双击
EVENT_RBUTTONDBLCLK 8 右键双击
EVENT_MBUTTONDBLCLK 9 中键双击
EVENT_MOUSEWHEEL 10 鼠标滚轮上下滚动
EVENT_MOUSEHWHEEL 11 鼠标左右滚动

flags:
EVENT_FLAG_LBUTTON 1 按下左键
EVENT_FLAG_RBUTTON 2 按下右键
EVENT_FLAG_MBUTTON 4 按下中键
EVENT_FLAG_CTRLKEY 8 按下ctrl键
EVENT_FLAG_SHIFTKEY 16 按下shift键
EVENT_FLAG_ALTKEY 32 按下alt键

  示例:

import cv2
import numpy as np

# 函数名可以随便取,但是参数必须是5个(参数名也可以随便取)
# event表示鼠标事件
# x, y是发生鼠标事件的坐标
# flags是鼠标的组合操作
def mouse_callback(event, x, y, flags, userdata):
    print(event, x, y, flags, userdata)
    # 按下鼠标右键退出
    if event == 2:
        cv2.destroyAllWindows()
    
# 创建窗口
cv2.namedWindow('mouse', cv2.WINDOW_NORMAL)
cv2.resizeWindow('mouse', 640, 360)

# 设置鼠标的回调函数
cv2.setMouseCallback('mouse', mouse_callback, '123')

# 生成全黑的图片
img = np.zeros((360, 640, 3), np.uint8)

while True:
    cv2.imshow('mouse', img)
    key = cv2.waitKey(1)
    if key == ord('q'):
        break
        
cv2.destroyAllWindows()

五、TrackBar控件

image-20240926102921673

  • createTrackbar(trackbarname, winname, value, count, onChange):创建TrackBar控件,value为trackbar的默认值,count为bar的最大值,最小为0。
  • getTrackbarPos(trackbarname, winname):获取TrackBar当前值。

  示例:

import cv2
import numpy as np

# 创建窗口
cv2.namedWindow('trackbar', cv2.WINDOW_NORMAL)
cv2.resizeWindow('trackbar', 640, 480)

# 定义回调函数
def callback(value):
    print(value)
    
# 创建trackbar
cv2.createTrackbar('R', 'trackbar', 0, 255, callback)
cv2.createTrackbar('G', 'trackbar', 0, 255, callback)
cv2.createTrackbar('B', 'trackbar', 0, 255, callback)

# 创建一个背景图片
img = np.zeros((480, 640, 3), np.uint8)

while True:
    # 获取当前trackbar的值
    r = cv2.getTrackbarPos('R', 'trackbar')
    g = cv2.getTrackbarPos('G', 'trackbar')
    b = cv2.getTrackbarPos('B', 'trackbar')
    
    # 改变背景图颜色
    img[:] = [b, g, r]
    cv2.imshow('trackbar', img)
    
    key = cv2.waitKey(1)
    if key & 0xFF == ord('q'):
        break
        
cv2.destroyAllWindows()

http://www.kler.cn/a/321371.html

相关文章:

  • 重拾设计模式--外观模式
  • 北京中小学信息学编程能力测评 BCSP-X 2024 下半年 真题汇总
  • leetcode212. 单词搜索 II
  • 《庐山派从入门到...》板载按键启动!
  • SQL进阶技巧:如何计算商品需求与到货队列表进出计划?
  • 中国人工智能学会技术白皮书
  • 通信工程学习:什么是VIM虚拟化基础设施管理器
  • 操作配置笔记
  • 网络与信息安全工程师(工信部教育与考试中心)
  • 【网络安全】Drupal之缓存中毒+储存型XSS
  • 实时湖仓架构演变
  • 【最基础最直观的排序 —— 选择排序算法】
  • 进阶SpringBoot之 Dubbo-admin 安装测试
  • Node-GDAL:简洁强大的Node.js地理空间数据处理库
  • 什么是Node.js?
  • ElasticSearch的安装与使用
  • EasyCVR智慧公园视频智能管理方案:赋能公园安全管理新高度
  • Spring中一些常见注解的作用
  • 使用ucharts写的小程序页面柱状图上方没有数字
  • 7款国内AI搜索引擎大全网站
  • 说说海外云手机的自动化功能
  • [Redis][哨兵][上]详细讲解
  • 深入浅出MongoDB(一)
  • 嵌入式C语言自我修养:GNU C编译器扩展语法精讲
  • 利用Puppeteer-Har记录与分析网页抓取中的性能数据
  • TikTok的网络环境搭建指南