参考
sdl安装测试-mingw32环境
mingW32环境用opencv测摄像头
ubuntu18.04环境用opencv测摄像头
摄像头发现
bash
复制代码
root@ant:~# lsusb
Bus 001 Device 003: ID 1bcf:2281 Sunplus Innovation Technology Inc.
Bus 001 Device 002: ID 05e3:0608 Genesys Logic, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
root@ant:~# ls /dev/video*
/dev/video0 /dev/video1 /dev/video2
root@ant:~# v4l2-ctl --list-devices
vcap_dvp output 0 (platform:vcap_dvp:0):
/dev/video0
Q8 HD Webcam: Q8 HD Webcam (usb-ci_hdrc.0-1.4):
/dev/video2
/dev/video3
简单测试
cam_test.py 拍照
bash
复制代码
import cv2
cap = cv2.VideoCapture(2)
# 强制 MJPEG(很多USB摄像头必须)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
ret, frame = cap.read()
if ret:
cv2.imwrite("test.jpg", frame)
print("OK")
else:
print("FAIL")
cap.release()
录像
bash
复制代码
#!/usr/bin/env python3
"""
ZYNQ 摄像头无显示器版本(稳定版)
- SSH/CLI 环境完全适配
- 支持命令行传入摄像头号
- 单帧、批量、录像、连续监控
"""
import cv2
import sys
import time
import json
import os
from pathlib import Path
from datetime import datetime
# ---------------------------
# 获取摄像头号
# ---------------------------
CAM_ID = int(sys.argv[1]) if len(sys.argv) > 1 else 1
# ---------------------------
# 摄像头类
# ---------------------------
class HeadlessCameraCapture:
def __init__(self, camera_id=1, output_dir="./captures", enable_metadata=True, verbose=True):
self.camera_id = camera_id
self.output_dir = output_dir
self.enable_metadata = enable_metadata
self.verbose = verbose
self.cap = None
self.frame_count = 0
Path(self.output_dir).mkdir(parents=True, exist_ok=True)
def log(self, msg, level="INFO"):
if not self.verbose:
return
levels = {"INFO": "✓", "WARN": "⚠", "ERROR": "❌"}
prefix = levels.get(level, "•")
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"[{timestamp}] {prefix} {msg}")
def open_camera(self, width=640, height=480, fps=10, retries=5):
self.log(f"正在打开摄像头 {self.camera_id}...")
self.cap = cv2.VideoCapture(f"/dev/video{self.camera_id}")
if not self.cap.isOpened():
self.log("❌ 无法打开摄像头", "ERROR")
return False
# 强制 MJPG + 分辨率 + FPS
self.cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
self.cap.set(cv2.CAP_PROP_FPS, fps)
time.sleep(0.5) # 等待摄像头稳定
# 尝试读取一帧
for i in range(retries):
ret, frame = self.cap.read()
if ret:
self.log(f"✓ 摄像头打开成功,分辨率: {frame.shape[1]}x{frame.shape[0]}")
return True
time.sleep(0.1)
self.log("❌ 摄像头读取失败", "ERROR")
self.cap.release()
self.cap = None
return False
def capture_single_frame(self, filename=None):
if not self.cap:
self.log("❌ 摄像头未打开", "ERROR")
return None
ret, frame = self.cap.read()
if not ret or frame is None:
self.log("❌ 读取失败", "WARN")
return None
self.frame_count += 1
if filename is None:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3]
filename = f"frame_{self.camera_id}_{timestamp}.jpg"
filepath = os.path.join(self.output_dir, filename)
cv2.imwrite(filepath, frame)
self.log(f"✓ 已保存: {filename} ({os.path.getsize(filepath)/1024:.1f}KB)")
if self.enable_metadata:
self._save_metadata(filepath, frame)
return filepath
def capture_video(self, duration=10, fps=10, output_file=None):
if not self.cap:
self.log("❌ 摄像头未打开", "ERROR")
return None
if output_file is None:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_file = f"record_{self.camera_id}_{timestamp}.avi"
output_path = os.path.join(self.output_dir, output_file)
width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
self.log(f"开始录像: {output_file}, 时长 {duration}s")
start_time = time.time()
frame_count = 0
try:
while time.time() - start_time < duration:
ret, frame = self.cap.read()
if ret:
out.write(frame)
frame_count += 1
finally:
out.release()
self.log(f"录像完成: {output_file}, 共 {frame_count} 帧")
return output_path
def _save_metadata(self, image_path, frame):
try:
json_path = image_path.replace('.jpg', '_meta.json')
metadata = {
'timestamp': datetime.now().isoformat(),
'camera_id': self.camera_id,
'resolution': f"{frame.shape[1]}x{frame.shape[0]}",
'file_size_kb': os.path.getsize(image_path)/1024,
'frame_count': self.frame_count
}
with open(json_path, 'w') as f:
json.dump(metadata, f, indent=2)
except:
pass
# ---------------------------
# 简单测试录像
# ---------------------------
if __name__ == "__main__":
cam = HeadlessCameraCapture(camera_id=CAM_ID)
if cam.open_camera(width=640, height=480, fps=10):
cam.capture_video(duration=20, fps=10)
cam.cap.release()
网络摄像头
访问 http://192.168.3.42:8080/video
python
复制代码
#!/usr/bin/env python3
"""
ZYNQ 摄像头浏览器访问版(纯 Python + OpenCV,无依赖)
- MJPEG 流浏览器访问
- 显示帧数和实时 FPS
- 可选录像
"""
import cv2
import time
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
CAM_ID = 3
FRAME_WIDTH = 640
FRAME_HEIGHT = 480
FPS = 10
SHOW_FPS = True
RECORD = False
PORT = 8080
class CameraStream:
def __init__(self, camera_id=0, width=640, height=480, fps=10):
self.cap = cv2.VideoCapture(f"/dev/video{camera_id}", cv2.CAP_V4L2)
if not self.cap.isOpened():
raise RuntimeError(f"无法打开摄像头 /dev/video{camera_id}")
self.cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
self.cap.set(cv2.CAP_PROP_FPS, fps)
self.frame = None
self.lock = threading.Lock()
self.stopped = False
# FPS
self._last_time = time.time()
self._frame_count = 0
self.current_fps = 0
# 录像
self.out = None
if RECORD:
timestamp = time.strftime("%Y%m%d_%H%M%S")
self.out = cv2.VideoWriter(f'record_{camera_id}_{timestamp}.avi',
cv2.VideoWriter_fourcc(*'MJPG'),
fps,
(width, height))
t = threading.Thread(target=self.update, daemon=True)
t.start()
def update(self):
while not self.stopped:
ret, frame = self.cap.read()
if not ret:
continue
self._frame_count += 1
now = time.time()
if now - self._last_time >= 1.0:
self.current_fps = self._frame_count / (now - self._last_time)
self._frame_count = 0
self._last_time = now
if SHOW_FPS:
cv2.putText(frame, f'FPS: {self.current_fps:.1f}', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
cv2.putText(frame, f'Frame: {self._frame_count}', (10, 60),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
if self.out:
self.out.write(frame)
with self.lock:
self.frame = frame
def read(self):
with self.lock:
if self.frame is None:
return None
ret, jpeg = cv2.imencode('.jpg', self.frame)
return jpeg.tobytes()
def stop(self):
self.stopped = True
time.sleep(0.1)
self.cap.release()
if self.out:
self.out.release()
# -------------------------
# HTTP MJPEG 服务
# -------------------------
camera = CameraStream(camera_id=CAM_ID, width=FRAME_WIDTH, height=FRAME_HEIGHT, fps=FPS)
class MJPEGHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path != '/video':
self.send_error(404)
return
self.send_response(200)
self.send_header('Content-type', 'multipart/x-mixed-replace; boundary=frame')
self.end_headers()
try:
while True:
frame = camera.read()
if frame is None:
continue
self.wfile.write(b'--frame\r\n')
self.send_header('Content-Type', 'image/jpeg')
self.send_header('Content-Length', str(len(frame)))
self.end_headers()
self.wfile.write(frame)
self.wfile.write(b'\r\n')
time.sleep(1.0 / FPS)
except Exception:
pass
if __name__ == '__main__':
print(f"访问浏览器查看摄像头:http://0.0.0.0:{PORT}/video")
server = HTTPServer(('0.0.0.0', PORT), MJPEGHandler)
try:
server.serve_forever()
finally:
camera.stop()
server.server_close()