摄像头模块简介

作者

尼拉夫·帕特尔著

联系方式

nrp@eclecti.cc

PYGAME 1.9支持接口摄像头,允许您捕获静态图像、观看实况流,以及进行一些简单的计算机视觉。本教程将涵盖所有这些用例,并提供可供您的应用程序或游戏使用的代码示例。您可以参考 reference documentation 获取完整的API。

备注

从PYGAME 1.9开始,摄像头模块为Linux上使用V4L2的摄像头提供本机支持。通过Video Capture或OpenCV可以支持其他平台,但本指南将重点介绍本机模块。大多数代码将在其他平台上有效,但某些功能(如控件)将不起作用。该模块还标记为 EXPERIMENTAL 这意味着API可能会在后续版本中发生变化。

导入和初始化

import pygame
import pygame.camera
from pygame.locals import *

pygame.init()
pygame.camera.init()

由于摄像头模块是可选的,因此需要手动导入和初始化,如上所示。

捕获单个图像

现在,我们将介绍最简单的情况,即打开相机并将帧捕获为曲面。在下面的示例中,我们假设计算机上的/dev/Video0处有一个摄像头,并将其初始化为640 x 480的大小。调用GET_IMAGE()时摄影机所看到的就是名为Image的曲面。**

cam = pygame.camera.Camera("/dev/video0",(640,480))
cam.start()
image = cam.get_image()

列出已连接的摄像头

你可能会想,如果我们不知道摄像机的确切路径怎么办?我们可以要求模块提供连接到计算机的摄像机列表,并初始化列表中的第一台摄像机。**

camlist = pygame.camera.list_cameras()
if camlist:
    cam = pygame.camera.Camera(camlist[0],(640,480))

使用摄像机控件

大多数摄像头都支持翻转图像和更改亮度等控制。在使用Start()之后,可以随时使用Set_Controls()和Get_Controls()。**

cam.set_controls(hflip = True, vflip = False)
print camera.get_controls()

捕获实时流

本教程的其余部分将围绕捕获实时图像流展开。为此,我们将使用下面的类。如上所述,它将简单地将持续不断的摄像头帧传输到屏幕上,有效地显示实时视频。这基本上是您所期望的,循环GET_IMAGE(),在显示表面上进行blit,然后翻转它。出于性能原因,我们将为相机提供相同的表面以供每次使用。**

class Capture:
    def __init__(self):
        self.size = (640,480)
        # create a display surface. standard pygame stuff
        self.display = pygame.display.set_mode(self.size, 0)

        # this is the same as what we saw before
        self.clist = pygame.camera.list_cameras()
        if not self.clist:
            raise ValueError("Sorry, no cameras detected.")
        self.cam = pygame.camera.Camera(self.clist[0], self.size)
        self.cam.start()

        # create a surface to capture to.  for performance purposes
        # bit depth is the same as that of the display surface.
        self.snapshot = pygame.surface.Surface(self.size, 0, self.display)

    def get_and_flip(self):
        # if you don't want to tie the framerate to the camera, you can check
        # if the camera has an image ready.  note that while this works
        # on most cameras, some will never return true.
        if self.cam.query_image():
            self.snapshot = self.cam.get_image(self.snapshot)

        # blit it to the display surface.  simple!
        self.display.blit(self.snapshot, (0,0))
        pygame.display.flip()

    def main(self):
        going = True
        while going:
            events = pygame.event.get()
            for e in events:
                if e.type == QUIT or (e.type == KEYDOWN and e.key == K_ESCAPE):
                    # close the camera safely
                    self.cam.stop()
                    going = False

            self.get_and_flip()

由于GET_IMAGE()是一个阻塞调用,在慢速相机上可能需要相当长的时间,因此本例使用Query_Image()来查看相机是否已准备好。这使您可以将游戏的帧速率与相机的帧速率分开。如果您发现您的摄影机不正确地支持QUERY_IMAGE()函数,也可以让摄影机在单独的线程中捕获图像,以获得大致相同的性能提升。

计算机视觉基础

通过使用相机、变换和遮罩模块,pyGame可以进行一些基本的计算机视觉。

色彩空间

在初始化相机时,Colorspace是一个可选参数,可以选择‘RGB’、‘YUV’和‘HSV’。对于计算机视觉来说,YUV和HSV通常都比RGB更有用,并且允许您更容易地根据颜色设置阈值,这一点我们将在本教程的后面部分看到。

self.cam = pygame.camera.Camera(self.clist[0], self.size, "RGB")
../_images/camera_rgb.jpg
self.cam = pygame.camera.Camera(self.clist[0], self.size, "YUV")
../_images/camera_yuv.jpg
self.cam = pygame.camera.Camera(self.clist[0], self.size, "HSV")
../_images/camera_hsv.jpg

阈值设置

使用Transform模块中的Threshold()函数,可以实现简单的类似绿色屏幕的效果,或在场景中隔离特定颜色的对象。在下面的示例中,我们只将绿色的树设置为阈值,并将图像的其余部分设置为黑色。有关的详细信息,请查看参考文档 threshold function .

self.thresholded = pygame.surface.Surface(self.size, 0, self.display)
self.snapshot = self.cam.get_image(self.snapshot)
pygame.transform.threshold(self.thresholded,self.snapshot,(0,255,0),(90,170,170),(0,0,0),2)
../_images/camera_thresholded.jpg

当然,这仅在您已经知道要查找的对象的确切颜色时才有用。为了解决这一问题并使阈值在现实世界中可用,我们需要添加一个校准阶段,在那里我们识别对象的颜色并使用它来对其进行阈值处理。我们将使用转换模块的Average_COLOR()函数来完成此操作。下面是一个示例校准函数,您可以循环该函数,直到发生像按键这样的事件,以及它将是什么样子的图像。框内的颜色将是用于阈值的颜色。请注意,我们在下图中使用的是HSV色彩空间。

def calibrate(self):
    # capture the image
    self.snapshot = self.cam.get_image(self.snapshot)
    # blit it to the display surface
    self.display.blit(self.snapshot, (0,0))
    # make a rect in the middle of the screen
    crect = pygame.draw.rect(self.display, (255,0,0), (145,105,30,30), 4)
    # get the average color of the area inside the rect
    self.ccolor = pygame.transform.average_color(self.snapshot, crect)
    # fill the upper left corner with that color
    self.display.fill(self.ccolor, (0,0,50,50))
    pygame.display.flip()
../_images/camera_average.jpg
pygame.transform.threshold(self.thresholded,self.snapshot,self.ccolor,(30,30,30),(0,0,0),2)
../_images/camera_thresh.jpg

您可以使用相同的想法来制作简单的绿屏/蓝屏,方法是首先获取背景图像,然后对其进行阈值处理。下面的示例将相机指向HSV色彩空间中的一面空白白墙。

def calibrate(self):
    # capture a bunch of background images
    bg = []
    for i in range(0,5):
      bg.append(self.cam.get_image(self.background))
    # average them down to one to get rid of some noise
    pygame.transform.average_surfaces(bg,self.background)
    # blit it to the display surface
    self.display.blit(self.background, (0,0))
    pygame.display.flip()
../_images/camera_background.jpg
pygame.transform.threshold(self.thresholded,self.snapshot,(0,255,0),(30,30,30),(0,0,0),1,self.background)
../_images/camera_green.jpg

使用遮罩模块

如果您只想显示图像,上面的内容是很棒的,但使用 mask module ,您还可以使用摄像头作为游戏的输入设备。例如,回到对特定对象设置阈值的示例,我们可以找到该对象的位置,并使用它来控制屏幕上的对象。

def get_and_flip(self):
    self.snapshot = self.cam.get_image(self.snapshot)
    # threshold against the color we got before
    mask = pygame.mask.from_threshold(self.snapshot, self.ccolor, (30, 30, 30))
    self.display.blit(self.snapshot,(0,0))
    # keep only the largest blob of that color
    connected = mask.connected_component()
    # make sure the blob is big enough that it isn't just noise
    if mask.count() > 100:
        # find the center of the blob
        coord = mask.centroid()
        # draw a circle with size variable on the size of the blob
        pygame.draw.circle(self.display, (0,255,0), coord, max(min(50,mask.count()/400),5))
    pygame.display.flip()
../_images/camera_mask.jpg

这只是最基本的例子。您可以跟踪多个不同颜色的水滴,找到对象的轮廓,在现实生活和游戏对象之间进行碰撞检测,获得对象的角度以实现更精细的控制,等等。玩得开心!




Edit on GitHub