如何在不使用OpenCV的情况下用Python捕获摄像头画面
替代OpenCV捕获摄像头单帧的轻量方案
以下方案均无需依赖OpenCV,编译后文件体积可控,分平台实现:
Windows平台(基于pywin32 + Pillow)
依赖仅为pywin32和Pillow,两者体积远小于OpenCV。示例代码:
import win32video from PIL import Image import numpy as np # 枚举摄像头设备 def get_camera_device(): devices = win32video.videoInput() device_count = devices.listDevices() if device_count == 0: raise Exception("未检测到摄像头") # 返回第一个摄像头设备索引 return 0 # 捕获单帧 def capture_frame(device_idx): devices = win32video.videoInput() devices.setupDevice(device_idx) # 获取帧数据 width, height = devices.getWidth(device_idx), devices.getHeight(device_idx) frame_data = np.zeros((height, width, 3), dtype=np.uint8) devices.getImage(device_idx, frame_data.ctypes.data, width, height, 3) devices.stopDevice(device_idx) # 转换为PIL Image并保存 img = Image.fromarray(frame_data) img.save("captured_frame.jpg") if __name__ == "__main__": cam_idx = get_camera_device() capture_frame(cam_idx)
安装依赖:pip install pywin32 pillow
Linux平台(基于V4L2原生API)
直接调用Linux内核的V4L2接口,仅需标准库和numpy(可选,也可手动处理字节流)。示例代码:
import os import fcntl import mmap import numpy as np from PIL import Image # V4L2常量定义 VIDIOC_S_FMT = 0xc00c5605 VIDIOC_REQBUFS = 0xc0085608 VIDIOC_QUERYBUF = 0x80085609 VIDIOC_QBUF = 0xc008560a VIDIOC_DQBUF = 0x8008560b VIDIOC_STREAMON = 0x40045607 def capture_frame(): # 打开摄像头设备(通常为/dev/video0) fd = os.open("/dev/video0", os.O_RDWR) # 设置格式为RGB24 fmt = { "type": 1, # V4L2_BUF_TYPE_VIDEO_CAPTURE "width": 640, "height": 480, "pixelformat": 0x32344752, # RGB24 "field": 0, "bytesperline": 640*3, "sizeimage": 640*480*3, "colorspace": 0, } fcntl.ioctl(fd, VIDIOC_S_FMT, fmt) # 请求缓冲区 reqbufs = {"count": 1, "type": 1, "memory": 1} # V4L2_MEMORY_MMAP fcntl.ioctl(fd, VIDIOC_REQBUFS, reqbufs) # 查询缓冲区 buf = {"index": 0, "type": 1, "memory": 1, "length": 0, "offset": 0} fcntl.ioctl(fd, VIDIOC_QUERYBUF, buf) # 映射缓冲区 mm = mmap.mmap(fd, buf["length"], mmap.MAP_SHARED, mmap.PROT_READ, offset=buf["offset"]) # 入队缓冲区并启动流 fcntl.ioctl(fd, VIDIOC_QBUF, buf) fcntl.ioctl(fd, VIDIOC_STREAMON, 1) # 出队获取帧 fcntl.ioctl(fd, VIDIOC_DQBUF, buf) frame_data = np.frombuffer(mm, dtype=np.uint8).reshape(480, 640, 3) # 停止流并清理 fcntl.ioctl(fd, VIDIOC_STREAMON, 0) mm.close() os.close(fd) # 保存帧 img = Image.fromarray(frame_data) img.save("captured_frame.jpg") if __name__ == "__main__": capture_frame()
依赖安装:pip install numpy pillow(若手动处理字节流可无需numpy)
macOS平台(基于PyObjC调用AVFoundation)
利用苹果原生AVFoundation框架,依赖仅为pyobjc-core和pyobjc-framework-AVFoundation,体积轻量。示例代码:
import objc import time from AVFoundation import ( AVCaptureSession, AVCaptureDevice, AVCaptureDeviceInput, AVCapturePhotoOutput, AVCapturePhotoSettings ) from PIL import Image from Foundation import NSData def capture_frame(): # 创建捕获会话 session = AVCaptureSession.alloc().init() session.setSessionPreset_(AVCaptureSession.PresetPhoto) # 获取默认摄像头 device = AVCaptureDevice.defaultDeviceWithMediaType_("vide") input_device = AVCaptureDeviceInput.deviceInputWithDevice_error_(device, None)[0] session.addInput_(input_device) # 设置照片输出 photo_output = AVCapturePhotoOutput.alloc().init() session.addOutput_(photo_output) # 定义回调处理捕获的照片 capture_done = False class PhotoCaptureDelegate(objc.NSObject): def captureOutput_didFinishProcessingPhoto_error_(self, output, photo, error): nonlocal capture_done if error: print(f"捕获失败: {error}") capture_done = True return # 获取图片数据并保存 image_data = photo.fileDataRepresentation() image_data.writeToFile_atomically_("captured_frame.jpg", True) session.stopRunning() capture_done = True delegate = PhotoCaptureDelegate.alloc().init() settings = AVCapturePhotoSettings.photoSettings() # 启动会话并捕获照片 session.startRunning() photo_output.capturePhotoWithSettings_delegate_(settings, delegate) # 等待捕获完成 while not capture_done: time.sleep(0.1) if __name__ == "__main__": capture_frame()
安装依赖:pip install pyobjc-core pyobjc-framework-AVFoundation pillow
编译体积优化建议
使用PyInstaller编译时,可通过以下方式进一步缩小体积:
- 使用
--exclude-module排除未使用的模块,比如--exclude-module numpy(若代码中未用到) - 启用UPX压缩:
pyinstaller --onefile --upx-dir /path/to/upx your_script.py - 使用
--clean参数清理临时文件,避免冗余依赖打包
内容的提问来源于stack exchange,提问作者Dr. Dark Flames
相关产品推荐
相关产品推荐

