Chemmy's Blog

chengming0916@outlook.com

获取VideoCapture实例

1
2
3
4
5
6
7
8
# 读取视频流
strem_capture = cv2.VideoCapture("rtst://192.168.0.0/live/demo")

# 读取视频文件
file_capture = cv2.VideoCapture('demo.mp4')

# 读取摄像头
capture = cv2.VideoCapture(0)

获取摄像头编号可使用ls -al /dev/ |grep video,输出信息以video开头其后缀为数字即为可能的摄像头编号。

检查获取VideoCapture实例是否成功

1
2
3
# 校验获取VideoCapture类实例
if not capture.isOpened():
return

获取视频流信息

1
2
3
4
5
6
7
8
# 获取视频帧的宽
width = capture.get(cv2.CAP_PROP_FRAME_WIDTH)

# 获取视频帧的高
height = cpature.get(cv2.CAP_PROP_FRAME_HEIGHT)

# 获取视频帧率
fps = capture.get(cv2.CAP_PROP_FPS)

获取帧画面

1
success, frame = capture.read()

当需要同时处理多路摄像头时一般使用grab()retrieve()代替

1
2
3
4
5
6
7
success_1 = capture.grab()
success_2 = stream_capture.grab()

if success_1 and success_2:
frame_1 = capture.retrieve()
frame_2 = stream_capture.retrieve()

设置分辨率

1
2
3
4
5
# 设置摄像头分辨率的宽
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)

# 设置摄像头分辨率的高
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)

保存视频文件

无论是视频文件存储还是摄像头画面保存都是用VideoWriter类,初始化时需要传入文件名(包含文件格式)、视频编解码器、视频保存帧率 、分辨率,保存视频的帧率最好和读入的帧率一致,分辨率可以更改,只是要求写入的帧大小要与分辨率保持一致。
若指定的文件名已存在则会覆盖文件。

1
2
3
writer = cv2.VideoWriter('output.mp4', 
cv2.VideoWriter_fourcc(*'MP4V'), 30, (1080,1920))
writer.write(frame)

释放资源

不管是VideoCapture还是VideoWriter类,使用完都应该释放资源

1
2
3
4
5
# 释放VideoCapture资源
capture.release()

# 释放VideoWriter资源
writer.release()

完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# -*- coding: utf-8 -*-
# /usr/bin/env/python3

import cv2
import time

capture = cv2.VideoCapture('rtsp://192.168.0.0/live/demo)
fourcc = cv2.VideoWriter_fourcc(*'MP$v') # 或H264,H265
fps = capture.get(cv2.CAP_PROP_FPS)
width = capture.get(cv2.CAP_PROP_FRAME_WIDTH)
height = capture.get(cv2.CAP_PROP_FRAME_HEIGHT)

writer = cv2.VideoWriter('demo.mp4', fourcd, fps, (height, width))


while True:
if not capture.isOpened():
time.sleep(0.5)
continue
success, frame = capture.read()
if success:
cv2.imshow('DEMO', frame) # 显示画面
writer.write(frame) # 保存视频文件
if (cv2.waitKey(20) & 0xff) == ord('q'): # 等待20ms并判断是否按下'q'退出,waitkey只能传入整数,
break

capture.release() # 释放VideoCapture
writer.release() # 释放VideoWriter
cv2.destroyAllWindows() # 销毁所有opencv显示窗口

环境安装

此处使用设备 Tesla P40 + Debian 12为例

显卡驱动:下载 NVIDIA 官方驱动 | NVIDIA
CUDA Toolkit: CUDA Toolkit Archive | NVIDIA Developer
![[Docke种使用GPU运行Ollama/IMG-20260105103041635.png]]
注意CUDA版本对应,否则可能会导致CUDA在容器内无法运行

1
2
3
4
5
6
7
8
9
10
11
12
13
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

sudo apt-get update

export NVIDIA_CONTAINER_TOOLKIT_VERSION=1.18.1-1
sudo apt-get install -y \
nvidia-container-toolkit=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
nvidia-container-toolkit-base=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
libnvidia-container-tools=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
libnvidia-container1=${NVIDIA_CONTAINER_TOOLKIT_VERSION}
  • 禁用 nouveau
    参考[[../Linux/Ubuntu禁用Nouveau驱动|Ubuntu禁用Nouveau驱动]]

  • 验证环境是否安装成功

    1
    docker run --rm --gpus all nvidia/cuda:12.2.0-runtime-ubuntu22.04 nvidia-smi

![[Docke种使用GPU运行Ollama/IMG-20260105103041714.png]]

直接运行Docker容器

1. 运行Ollama容器

1
docker run --gpus=all -d -e OLLAMA_MODEL:qwen2.5-coder:7b -e OLLAMA_ENV:production -v=qwen-coder:/root/.ollama -p 11437:11434 --name qwen-coder -d ollama/ollama:latest

2. 拉取模型并运行

1
ollama run qwen2.5-coder:7b

使用Docker Compose

1. 创建docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
services:
ollama:
container_name: qwen2.5-coder-7b
image: ollama/ollama:latest
ports:
- 11434:11431
volumes:
- ./data:/root/.ollama
deploy:
resources:
devices:
- driver: nvidia
capabilities: ["gpu"]
environment:
OLLAMA_MODEL: "qwen2.5-coder:7b"
restart: unless-stopped

2. 启动服务

docker-compose.yml所在目录运行以下命令:

1
docker-compose up -d

验证是否使用GPU启动Ollama

1
2
docker exec -it <ollama容器ID> bash # 进入已启动的容器
ollama ps

如果PROCESSOR中显示为GPU则启动成功
![[Docke种使用GPU运行Ollama/IMG-20260105103041772.png]]

Ollama运行deepseek-r1示例

1
2
3
4
5
6
7
8
9
10
# CPU运行
docker run -d -v deepseek-r1:/root/.ollama -p 11434:11434 --name deepseek-r1 ollama/ollama:latest

# GPU运行
docker run -d -v deepseek-r1:/root/.ollama -p 11434:11434 --name deepseek-r1-gpu ollama/ollama:latest

# 如果多张显卡运行需要添加负载均衡
-e OLLAMA_NUM_GPU:2 # 启用GPU数量(需与CUDA_VISIBLE_DEVICES匹配)
-e OLLAMA_SCHED_SPREAD:1 # 自动负载均衡

注意事项

  • 使用_nvidia-smi_确认GPU是否被正确利用。
  • 如果仅需CPU支持,可移除_–gpus=all_或相关配置。
  • GPU模式下性能显著提升,但需确保驱动和CUDA版本兼容。

1. 创建黑名单配置文件

1
sudo nano /etc/modprobe.d/blacklist-nouveau.conf
  • 添加以下内容以禁用Nouveau:
1
2
blacklist nouveau
options nouveau modeset=0

2. 更新initramfs

  • 更新内核的initramfs文件以应用更改:
    1
    sudo update-initramfs -u

3. 重启系统

  • 执行以下命令重启系统:
1
sudo reboot

4. 验证禁用状态

  • 重启后,检查Nouveau模块是否已禁用:

    1
    lsmod | grep nouveau
  • 如果没有输出,说明Nouveau已成功禁用。

注意事项

  • 禁用Nouveau后,可以安装NVIDIA专有驱动以获得更好的性能。

  • 如果需要恢复Nouveau驱动,只需删除_/etc/modprobe.d/blacklist-nouveau.conf_文件并重新更新initramfs。

货币格式

1
2
3
4
5
<!-- 默认保留两位小数 输出: $12.34 -->
<TextBlock Text="{Binding Price, StringFormat={}{0:C}}" />

<!-- 保留一位小数 输出: $123.4 -->
<TextBlock Text="{Binding Price, StringFormat={}{0:C1}}" />

固定文字

1
2
3
4
5
6
<!-- 固定前缀 输出: 单价:$12.34 -->
<TextBlock Text="{Binding Price, StringFormat=单价: {0:C}}" />

<!-- 固定后缀 输出: 12.345元 -->
<TextBlock Text="{Binding Price, StringFormat={}{0}元}" />

数字格式化

1
2
3
4
5
6
7
8
9
10
11
<!-- 固定位数,仅支持整形 输出: 086723 -->
<TextBlock Text="{Binding Total, StringFormat={}{0:D6}}" />

<!-- 固定小数点后位数 输出: 8234.9354 -->
<TextBlock Text="{Binding Total, StringFormat={}{0:F4}}" />

<!-- 使用用分割符并指定小数点后位数 输出: 8234.933 -->
<TextBlock Text="{Binding Total, StringFormat={}{0:N3}}" />

<!-- 格式化百分比 输出: 78.9%-->
<TextBlock Text="{Binding Persent, StringFormat={}{0:P1}}" />

占位符

1
2
3
4
<!-- 输出: 0123.46 -->
<TextBox Text="{Binding Price, StringFormat={}{0:0000.00}}" />
<!-- 输出: 123.46 -->
<TextBox Text="{Binding Price, StringFormat={}{0:####.##}}" />

时间日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- 输出: 5/4/2015 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:d}}" />
<!-- 输出: Monday, May 04, 2015 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:D}}" />
<!-- 输出: Monday, May 04, 2015 5:46 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:f}}" />
<!-- 输出: Monday, May 04, 2015 5:46:56 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:F}}" />
<!-- 输出: 5/4/2015 5:46 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:g}}" />
<!-- 输出: 5/4/2015 5:46:56 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:G}}" />
<!-- 输出: May 04 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:m}}" />
<!-- 输出: May 04 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:M}}" />
<!-- 输出: 5:46 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:t}}" />
<!-- 输出: 5:46:56 PM -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:T}}" />
<!-- 输出: 2015年05月04日 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:yyyy年MM月dd日}}" />
<!-- 输出: 2015-05-04 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:yyyy-MM-dd}}" />
<!-- 输出: 2015-05-04 17:46 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:yyyy-MM-dd HH:mm}}" />
<!-- 输出: 2015-05-04 17:46:56 -->
<TextBox Text="{Binding DateTimeNow, StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}" />

多重绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--
\a &#x07; BEL
\b &#x08; BS - Backspace
\f &#x0c; FF - Formfeed
\n &#x0a; LF, NL - Linefeed, New Line
\r &#x0d; CR - Carriage return
\t &#x09; HT - Tab, Horizontal Tabelator
\v &#x0b; VT - Vertical Tabelator
-->
<TextBlock.Text>
<MultiBinding StringFormat="姓名: {0}&#x09;{1}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
</TextBlock.Text>
0%