使用其他按键来控制OpenCV的waitKey()函数

84

我正在处理一个程序(使用python和opencv),我使用空格键进入下一帧,使用Esc键退出程序。这是我能够工作的唯二按键。我试图找到其他按键的相关信息,尝试了各种代码,但没有成功,特别是方向键。

我在这里找到了与waitkey有关的信息,但是它并不起作用。

我的问题是,如何捕捉除escspacebar以外的其他按键,以触发我的Python-opencv程序中的特定函数?

12个回答

137
您可以在Python中使用ord()函数来实现这一点。
例如,如果您想触发“a”键的按下操作,请按以下步骤进行:
if cv2.waitKey(33) == ord('a'):
   print "pressed a"

这里有一份示例代码:绘制直方图

更新:

要查找任何键的键值,请使用以下简单脚本打印键值:

import cv2
img = cv2.imread('sof.jpg') # load a dummy image
while(1):
    cv2.imshow('img',img)
    k = cv2.waitKey(33)
    if k==27:    # Esc key to stop
        break
    elif k==-1:  # normally -1 returned,so don't print it
        continue
    else:
        print k # else print its value

使用此代码,我获得了以下值:

Upkey : 2490368
DownKey : 2621440
LeftKey : 2424832
RightKey: 2555904
Space : 32
Delete : 3014656
...... # Continue yourself :)

6
在OSX系统上,Up键的键值为63232,Down键的键值为63233,Left键的键值为63234,Right键的键值为63235。 - extensa5620
我感觉我没有收到任何特殊键的代码 - 这可能是因为我在Windows上使用Pyhton 3.8(IDLE IDE)运行它。 - Alexander Stohr
使用'hex()'函数时,通常更容易理解。 2490368=0x260000 2621440=0x280000 2424832=0x250000 2555904=0x270000 32=0x20 3014656=0x2e0000 - Alexander Stohr
对于OSX值: 63232=0xf700 63233=0xf701 63234=0xf702 63235=0xf703 - Alexander Stohr
请注意,较高位(您应该能够自己查找定义)是修改键的标志值,例如 alt/sys/meta/num-loc,并且可以独立于其他按键出现。某些平台不报告任何此类密钥。 - Alexander Stohr
显示剩余3条评论

26

waitKey返回的键码似乎与平台有关。然而,查看键返回什么可能非常有教育意义(顺便说一下,在我的平台上,Esc不返回27...)。

阿比德的答案列出的整数对于人类大脑来说大多无用(除非你是神童……)。但是,如果您以十六进制形式检查它们,或者看一下最低有效字节,您可能会注意到模式……

我用于检查waitKey返回值的脚本如下:

#!/usr/bin/env python

import cv2
import sys

cv2.imshow(sys.argv[1], cv2.imread(sys.argv[1]))
res = cv2.waitKey(0)
print('You pressed %d (0x%x), LSB: %d (%s)' % (res, res, res % 256,
    repr(chr(res%256)) if res%256 < 128 else '?'))

你可以将其用作最小的命令行图像查看器。

我得到的一些结果:

  • 字母 q:

    你按下了 1048689(0x100071),LSB:113('q')

  • Esc 键(传统上,ASCII 27):

    你按下了 1048603(0x10001b),LSB:27('\x1b')

  • 空格:

    你按下了 1048608(0x100020),LSB:32(' ')

这个列表可能还可以继续,但是当你获得“奇怪”的结果时,你可以看到前进的方式。

顺便说一句,如果你想把它放在一个循环中,你可以只使用 waitKey(0)(永远等待),而不是忽略 -1 的返回值。

编辑:这些高位有更多的含义 - 请参见 Andrew C 的答案(提示:它与键盘修饰键有关,例如所有的“锁定”键,如 NumLock)。

然而,我的最近的经验表明,存在平台依赖性 - 例如,来自 Anaconda 的 Python 3.6 上的 OpenCV 4.1.0 在 Windows 上不会产生这些位,并且对于一些(重要的)键,它从 waitKey() 返回 0(箭头,HomeEndPageDnPageUp,甚至是 DelIns)。至少 Backspace 返回 8(但...为什么不是 Del?)。

因此,对于跨平台的 UI,你可能只能使用 WASD、字母、数字、EscSpaceBackspace ;)


5
平台依赖性问题似乎是有符号/无符号整数的问题。可以使用按位运算很容易地解决它,例如 res = cv2.waitkey() & 0xEFFFFF - billyjmc
我没有仔细检查过,但从我的历史记录来看,我认为一些高位被正式声明为标志位。为了正确解释和掩码值,请搜索网络。(这绝对不是随意的!) - Alexander Stohr
@billyjmc - 不,这不是符号位。符号位是0x8而不是0x1。这些位是键盘修饰符的标志。清除符号位将是& 0x7fff...。 - Tomasz Gandor

17

已经发布的答案表明,waitKey 得到的一些不寻常值是由于平台差异。以下我提出(至少在某些平台上),waitKey的表现貌似奇怪是由于键盘修改器的原因。这篇文章看起来和Tomasz的回答类似,因为我最初将其作为编辑编写,但遭到了拒绝。


waitKey返回的键码会根据启用哪些修饰符而发生变化。NumLock、CapsLock 和 Shift、Ctrl、Alt 键都通过在两个最小有效字节之上启用某些位来修改 waitKey 返回的键码。其中最小的标志是Shift,在0x10000处。

下面是Tomasz发布的脚本的修改版本:

#!/usr/bin/env python

import cv2
import sys

cv2.imshow(sys.argv[1], cv2.imread(sys.argv[1]))
res = cv2.waitKey(0)
print 'You pressed %d (0x%x), 2LSB: %d (%s)' % (res, res, res % 2**16,
    repr(chr(res%256)) if res%256 < 128 else '?')

以下是相应的结果:

  • 使用NumLock键的q字母:

    您按下了1048689(0x100071),2LSB:113('q')

  • CapsLock但未使用NumLock的Escape键:

    您按下了131099(0x2001b),2LSB:27('\ x1b')

  • 使用Shift和NumLock的空格键:

    您按下了1114144(0x110020),2LSB:32(' ')

  • 使用Control,NumLock关闭的右箭头键:

    您按下了327507(0x4ff53),2LSB:65363('S')

我希望这有助于解释waitKey的不寻常行为以及如何获取实际按下的键,而不考虑NumLock和CapLock的状态。从这里开始,做一些类似以下的事情相对简单:

ctrlPressed = 0 != res & (1 << 18)

...作为“控制键”标志的是(将最低有效位视为位0)第18位。Shift在第16位,CapsLock的状态在第17位,Alt在第19位,NumLock在第20位。正如Tomasz所指出的那样,仅按下Shift键也会返回一个值,对于LShift和RShift分别有不同的值(仍然带有所有这些修饰符)。在依赖它们之前,请务必在自己的平台上双重检查所有这些修饰符和值。 :)


2
哇,原来是关于修饰符的问题...不错,我几乎从不关闭NumLock。顺便说一下:有时从0开始计算位更方便——即Shift位于第16位(掩码为(1<<16))。此外,只需按下Shift也会返回一个值——LShift为0xffe1,RShift为0xffe2。修饰符也适用于这里。 - Tomasz Gandor
1
不是所有的平台都会返回这些位 - 进行可移植编程的人可能非常清楚。 - Alexander Stohr

5

对于我来说,下面的代码无法正常工作,当它运行时,图片会在没有你按下按钮的情况下迅速跳转到下一张:

import cv2
img = cv2.imread('sof.jpg') # load a dummy image
while(1):
    cv2.imshow('img',img)
    k = cv2.waitKey(33)
    if k==27:    # Esc key to stop
        break
    elif k==-1:  # normally -1 returned,so don't print it
        continue
    else:
        print k # else print its value

但是这个有效:
def test_wait_key():
    lst_img_path = [
        '/home/xy/yy_face_head/face_det_test/111.png',
        '/home/xy/yy_face_head/face_det_test/222.png'
        #.....more path ...
    ]

    for f_path in lst_img_path:
        img = cv2.imread(f_path)
        cv2.imshow('tmp', img)
        c = cv2.waitKey(0) % 256

        if c == ord('a'):
            print "pressed a"
        else:
            print 'you press %s' % chr(c)

以下是输出结果:

在这里输入图片说明


输出如上。

5
有趣的是,没有人提到另一个Stack Overflow线程中提到的cv2.waitKeyEx() 。OpenCV关于cv2.waitKeyEx()的文档如下所述:

类似于waitKey,但返回完整的键代码。

注意

键代码是实现特定的,取决于使用的后端:QT/GTK/Win32等

因此,在跨平台实现上可能需要一些关注。然而,对我来说,这是迄今为止在Windows上获取箭头键等工作最简单直接的解决方案。

2
这是一个非常有用的贡献!查看OpenCV提交历史记录,waitKeyEx()此提交中于2016年被引入,这是在我留下初始评论之后。有趣的是,同一提交修补了原始的waitKey()函数,使其始终被掩码为code&0xff,这与我的答案中的res%2 ** 16具有相同的效果。并不令人惊讶,但非常有帮助的是,完整的按键代码取决于后端,这指向我们去获取更多信息的方向! - Andrew C.
2
这真是一个非常有帮助的贡献!查看OpenCV的提交历史,waitKeyEx()是在2016年的这个提交中引入的,而这是在我留下初始评论之后。有趣的是,同一个提交还修补了原始的waitKey()函数,使其始终被掩码为code & 0xff,这与我自己的答案中的res % 2**16具有相同的效果。并不奇怪,但将完整的按键码取决于后端明确说明了,这对于指引我们去寻找更多信息非常有帮助! - undefined

2

对于C++:

如果使用键盘字符/数字,更简单的解决方案是:

int key = cvWaitKey();

switch(key)
{
   case ((int)('a')):
   // do something if button 'a' is pressed
   break;
   case ((int)('h')):
   // do something if button 'h' is pressed
   break;
}

你不需要将字符转换为 int,只需使用 case 'a': 即可。 - eezstreet
当使用提高的警告级别进行编译时,您可能确实需要一些类型转换。相反,您应该从原始int值中提取一个“char”仅一次。这一步进一步可以去除任何用于修饰键的更高位。 - Alexander Stohr

2

在使用Ubuntu和C++时,我遇到了字符/整数转换的问题。我需要使用cv::waitKey()%256来获取正确的ASCII值。


请使用位运算:(cv:waitKey() & 255) - Alexander Stohr
如果使用非空超时,返回值为-1表示已达到超时,因此最好先将该值分配给一个变量,然后测试是否为-1,如果不是,则执行位剥离操作以获取所需的键码。 - Alexander Stohr

2

在Ubuntu18、python3和OpenCV 3.2.0上有效的答案与上面的答案类似。但是需要更改cv2.waitKey(0)这一行。这意味着程序将等待直到按下一个按钮。

使用此代码,我找到了箭头按钮的键值:向上箭头(82)、向下箭头(84)、向左箭头(81)和回车键(10)等。

import cv2
img = cv2.imread('sof.jpg') # load a dummy image
while(1):
    cv2.imshow('img',img)
    k = cv2.waitKey(0)
    if k==27:    # Esc key to stop
        break
    elif k==-1:  # normally -1 returned,so don't print it
        continue
    else:
        print k # else print its value

“-1”的返回值并不是“正常”的,它意味着超时已经发生。到目前为止,给出的评论并没有真正命中要点。除此之外:如果传递参数为0,则waitKey函数永远不会引发超时,因为它将一直等待,除非接收到按键。解码可能永远不会看到这些值 - 但从偏执编程的角度来看,并且为了随时可以选择替代超时,保持这种状态是一个很好的选择。 - Alexander Stohr

1

如果您想暂停程序以截取进度的屏幕截图

(在cv2.imshow中显示)

cv2.waitKey(0)会在按下“Scr”按钮(或其组合键)后继续,但您可以尝试这个方法

cv2.waitKey(0)
input('')

使用cv2.waitkey(0)可以给程序足够的时间来处理你想在imshow中看到的一切,而input('')则会让程序等待你在控制台窗口按下Enter键。

这适用于Python 3。


0

我也觉得这很令人困惑。 我正在运行Ubuntu 18,发现以下内容: 如果cv.imshow窗口具有焦点,则在终端中会得到一组值-如上面讨论的ASCII值。

如果终端具有焦点,则会看到不同的值。即,按a键时将看到“a”(而不是ASCII值97),按Escape键时将看到“^]”而不是“27”。

我在任何情况下都没有看到上述6位数字,并且我使用了类似的代码。 看来waitKey的值是以毫秒为单位的轮询周期。 这些点说明了这一点。

运行此片段并在测试图像上具有焦点时按键,然后单击终端窗口并按相同的键。

    import cv2
    img = cv2.imread('test.jpg') 
    cv2.imshow('Your test image', img)

    while(1):
      k = cv2.waitKey(300)
      if k == 27:
        break
      elif k==-1:
       print "."
       continue
      else:
        print k 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接