OpenCV 实用技巧三连:视频转图片、图片转视频、Pillow 与 OpenCV 互转

OpenCV 实用技巧三连:视频转图片、图片转视频、Pillow 与 OpenCV 互转

OpenCV 图像处理课程 · 第 9 章「Common Problem」学习笔记(彩蛋章节)

环境:Python 3.10 + opencv-python 4.5.5.64 + PyCharm(chapter9 目录)

包含 3 课:视频转图片 / 图片转视频 / Pillow 与 OpenCV 互转


章节总览

主题 核心函数 文件
01 视频转图片 os.makedirs() + cv2.imwrite() video_to_images.py
02 图片转视频 cv2.VideoWriter() + os.listdir() + cv2.resize() image_to_video.py
03 Pillow 转 OpenCV np.array() + cv2.cvtColor(RGB→BGR) read_image_from_pillow.py

本章说明:收录学生最常遇到的 3 个问题/技巧,都是视频与图像、不同图像库之间的相互转换。


第 1 课 · 视频转图片(Save Images From Videos)

需求

把一个视频文件的每一帧保存为图片,存到某个文件夹。

流程

#mermaid-svg-MqTvIm3sOAODsOOA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MqTvIm3sOAODsOOA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MqTvIm3sOAODsOOA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MqTvIm3sOAODsOOA .error-icon{fill:#552222;}#mermaid-svg-MqTvIm3sOAODsOOA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MqTvIm3sOAODsOOA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MqTvIm3sOAODsOOA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MqTvIm3sOAODsOOA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MqTvIm3sOAODsOOA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MqTvIm3sOAODsOOA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MqTvIm3sOAODsOOA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MqTvIm3sOAODsOOA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MqTvIm3sOAODsOOA .marker.cross{stroke:#333333;}#mermaid-svg-MqTvIm3sOAODsOOA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MqTvIm3sOAODsOOA p{margin:0;}#mermaid-svg-MqTvIm3sOAODsOOA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MqTvIm3sOAODsOOA .cluster-label text{fill:#333;}#mermaid-svg-MqTvIm3sOAODsOOA .cluster-label span{color:#333;}#mermaid-svg-MqTvIm3sOAODsOOA .cluster-label span p{background-color:transparent;}#mermaid-svg-MqTvIm3sOAODsOOA .label text,#mermaid-svg-MqTvIm3sOAODsOOA span{fill:#333;color:#333;}#mermaid-svg-MqTvIm3sOAODsOOA .node rect,#mermaid-svg-MqTvIm3sOAODsOOA .node circle,#mermaid-svg-MqTvIm3sOAODsOOA .node ellipse,#mermaid-svg-MqTvIm3sOAODsOOA .node polygon,#mermaid-svg-MqTvIm3sOAODsOOA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MqTvIm3sOAODsOOA .rough-node .label text,#mermaid-svg-MqTvIm3sOAODsOOA .node .label text,#mermaid-svg-MqTvIm3sOAODsOOA .image-shape .label,#mermaid-svg-MqTvIm3sOAODsOOA .icon-shape .label{text-anchor:middle;}#mermaid-svg-MqTvIm3sOAODsOOA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MqTvIm3sOAODsOOA .rough-node .label,#mermaid-svg-MqTvIm3sOAODsOOA .node .label,#mermaid-svg-MqTvIm3sOAODsOOA .image-shape .label,#mermaid-svg-MqTvIm3sOAODsOOA .icon-shape .label{text-align:center;}#mermaid-svg-MqTvIm3sOAODsOOA .node.clickable{cursor:pointer;}#mermaid-svg-MqTvIm3sOAODsOOA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MqTvIm3sOAODsOOA .arrowheadPath{fill:#333333;}#mermaid-svg-MqTvIm3sOAODsOOA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MqTvIm3sOAODsOOA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MqTvIm3sOAODsOOA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MqTvIm3sOAODsOOA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MqTvIm3sOAODsOOA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MqTvIm3sOAODsOOA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MqTvIm3sOAODsOOA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MqTvIm3sOAODsOOA .cluster text{fill:#333;}#mermaid-svg-MqTvIm3sOAODsOOA .cluster span{color:#333;}#mermaid-svg-MqTvIm3sOAODsOOA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MqTvIm3sOAODsOOA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MqTvIm3sOAODsOOA rect.text{fill:none;stroke-width:0;}#mermaid-svg-MqTvIm3sOAODsOOA .icon-shape,#mermaid-svg-MqTvIm3sOAODsOOA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MqTvIm3sOAODsOOA .icon-shape p,#mermaid-svg-MqTvIm3sOAODsOOA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MqTvIm3sOAODsOOA .icon-shape .label rect,#mermaid-svg-MqTvIm3sOAODsOOA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MqTvIm3sOAODsOOA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MqTvIm3sOAODsOOA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MqTvIm3sOAODsOOA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} images 文件夹
while cap.read()
视频文件
VideoCapture(path)
读取一帧 frame
cv2.imwrite 保存
frame_0.jpg, frame_1.jpg, ...

完整代码(video_to_images.py)

python 复制代码
import cv2
import os

video_path = "../data/lion_video.mp4"
cap = cv2.VideoCapture(video_path)

image_directory = "images"
if not os.path.exists(image_directory):   # 目录不存在则创建
    os.makedirs(image_directory)

count = 0
while True:
    ret, frame = cap.read()
    if ret:
        cv2.imwrite(f"{image_directory}/{count}.jpg", frame)  # 保存帧
        count += 1
        cv2.imshow("video2images", frame)
    else:
        break
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

cv2.destroyAllWindows()
cap.release()

要点

  • os.makedirs() 创建保存目录(os.path.exists 先检查)
  • 计数变量 count 为每帧生成唯一文件名
  • cv2.imwrite(文件名, 帧) 保存单帧;遍历循环把整部视频拆成帧

第 2 课 · 图片转视频(Make Video From Images)

需求

一文件夹的图片 合成一个视频。80% 学生在此踩坑(尺寸不匹配报错)。

流程

#mermaid-svg-5znVi9tw0wzUBOqS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5znVi9tw0wzUBOqS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5znVi9tw0wzUBOqS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5znVi9tw0wzUBOqS .error-icon{fill:#552222;}#mermaid-svg-5znVi9tw0wzUBOqS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5znVi9tw0wzUBOqS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5znVi9tw0wzUBOqS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5znVi9tw0wzUBOqS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5znVi9tw0wzUBOqS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5znVi9tw0wzUBOqS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5znVi9tw0wzUBOqS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5znVi9tw0wzUBOqS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5znVi9tw0wzUBOqS .marker.cross{stroke:#333333;}#mermaid-svg-5znVi9tw0wzUBOqS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5znVi9tw0wzUBOqS p{margin:0;}#mermaid-svg-5znVi9tw0wzUBOqS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5znVi9tw0wzUBOqS .cluster-label text{fill:#333;}#mermaid-svg-5znVi9tw0wzUBOqS .cluster-label span{color:#333;}#mermaid-svg-5znVi9tw0wzUBOqS .cluster-label span p{background-color:transparent;}#mermaid-svg-5znVi9tw0wzUBOqS .label text,#mermaid-svg-5znVi9tw0wzUBOqS span{fill:#333;color:#333;}#mermaid-svg-5znVi9tw0wzUBOqS .node rect,#mermaid-svg-5znVi9tw0wzUBOqS .node circle,#mermaid-svg-5znVi9tw0wzUBOqS .node ellipse,#mermaid-svg-5znVi9tw0wzUBOqS .node polygon,#mermaid-svg-5znVi9tw0wzUBOqS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5znVi9tw0wzUBOqS .rough-node .label text,#mermaid-svg-5znVi9tw0wzUBOqS .node .label text,#mermaid-svg-5znVi9tw0wzUBOqS .image-shape .label,#mermaid-svg-5znVi9tw0wzUBOqS .icon-shape .label{text-anchor:middle;}#mermaid-svg-5znVi9tw0wzUBOqS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5znVi9tw0wzUBOqS .rough-node .label,#mermaid-svg-5znVi9tw0wzUBOqS .node .label,#mermaid-svg-5znVi9tw0wzUBOqS .image-shape .label,#mermaid-svg-5znVi9tw0wzUBOqS .icon-shape .label{text-align:center;}#mermaid-svg-5znVi9tw0wzUBOqS .node.clickable{cursor:pointer;}#mermaid-svg-5znVi9tw0wzUBOqS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5znVi9tw0wzUBOqS .arrowheadPath{fill:#333333;}#mermaid-svg-5znVi9tw0wzUBOqS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5znVi9tw0wzUBOqS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5znVi9tw0wzUBOqS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5znVi9tw0wzUBOqS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5znVi9tw0wzUBOqS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5znVi9tw0wzUBOqS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5znVi9tw0wzUBOqS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5znVi9tw0wzUBOqS .cluster text{fill:#333;}#mermaid-svg-5znVi9tw0wzUBOqS .cluster span{color:#333;}#mermaid-svg-5znVi9tw0wzUBOqS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5znVi9tw0wzUBOqS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5znVi9tw0wzUBOqS rect.text{fill:none;stroke-width:0;}#mermaid-svg-5znVi9tw0wzUBOqS .icon-shape,#mermaid-svg-5znVi9tw0wzUBOqS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5znVi9tw0wzUBOqS .icon-shape p,#mermaid-svg-5znVi9tw0wzUBOqS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5znVi9tw0wzUBOqS .icon-shape .label rect,#mermaid-svg-5znVi9tw0wzUBOqS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5znVi9tw0wzUBOqS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5znVi9tw0wzUBOqS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5znVi9tw0wzUBOqS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 视频文件
for name in image_names
图片文件夹
os.listdir 列出所有图片名
拼路径 + cv2.imread
cv2.resize 对齐尺寸
out.write 写入
VideoWriter

完整代码(image_to_video.py)

python 复制代码
import cv2
import os

image_directory = "images"
image_names = os.listdir(image_directory)    # 列出所有图片文件名
print(image_names)

size = (500, 500)                            # 目标尺寸
out = cv2.VideoWriter("test.mp4",
                      cv2.VideoWriter_fourcc(*"mp4v"), 15, size)

for name in image_names:
    image_path = f"{image_directory}/{name}"       # 拼完整路径
    image = cv2.imread(image_path)
    image_resized = cv2.resize(image, size)        # 关键:先 resize 对齐
    out.write(image_resized)

out.release()

坑点

  • 视频尺寸必须和图片尺寸一致,否则播放报错
  • 修复:先 print(image.shape) 看实际尺寸,再用 cv2.resize(image, size) 对齐
  • 本课中图片是 1920×1080,指定的 (500,500) 不匹配 → 需先 resize
  • 结尾必须 out.release()

第 3 课 · Pillow 转 OpenCV(Image from Pillow to OpenCV)

需求

用 Pillow(PIL)读取的图像,如何在 OpenCV 中使用(保存/处理)。PIL 图不能直接用 OpenCV,需转换。

关键转换(两步)

  1. PIL 图 → numpy 数组np.array(image)(因为 OpenCV 图像是 ndarray)
  2. RGB → BGRcv2.cvtColor(img, cv2.COLOR_RGB2BGR)(PIL 用 RGB,OpenCV 用 BGR)

流程

#mermaid-svg-YYMhrntpUz4EgeYB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YYMhrntpUz4EgeYB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YYMhrntpUz4EgeYB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YYMhrntpUz4EgeYB .error-icon{fill:#552222;}#mermaid-svg-YYMhrntpUz4EgeYB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YYMhrntpUz4EgeYB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YYMhrntpUz4EgeYB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YYMhrntpUz4EgeYB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YYMhrntpUz4EgeYB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YYMhrntpUz4EgeYB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YYMhrntpUz4EgeYB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YYMhrntpUz4EgeYB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YYMhrntpUz4EgeYB .marker.cross{stroke:#333333;}#mermaid-svg-YYMhrntpUz4EgeYB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YYMhrntpUz4EgeYB p{margin:0;}#mermaid-svg-YYMhrntpUz4EgeYB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YYMhrntpUz4EgeYB .cluster-label text{fill:#333;}#mermaid-svg-YYMhrntpUz4EgeYB .cluster-label span{color:#333;}#mermaid-svg-YYMhrntpUz4EgeYB .cluster-label span p{background-color:transparent;}#mermaid-svg-YYMhrntpUz4EgeYB .label text,#mermaid-svg-YYMhrntpUz4EgeYB span{fill:#333;color:#333;}#mermaid-svg-YYMhrntpUz4EgeYB .node rect,#mermaid-svg-YYMhrntpUz4EgeYB .node circle,#mermaid-svg-YYMhrntpUz4EgeYB .node ellipse,#mermaid-svg-YYMhrntpUz4EgeYB .node polygon,#mermaid-svg-YYMhrntpUz4EgeYB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YYMhrntpUz4EgeYB .rough-node .label text,#mermaid-svg-YYMhrntpUz4EgeYB .node .label text,#mermaid-svg-YYMhrntpUz4EgeYB .image-shape .label,#mermaid-svg-YYMhrntpUz4EgeYB .icon-shape .label{text-anchor:middle;}#mermaid-svg-YYMhrntpUz4EgeYB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YYMhrntpUz4EgeYB .rough-node .label,#mermaid-svg-YYMhrntpUz4EgeYB .node .label,#mermaid-svg-YYMhrntpUz4EgeYB .image-shape .label,#mermaid-svg-YYMhrntpUz4EgeYB .icon-shape .label{text-align:center;}#mermaid-svg-YYMhrntpUz4EgeYB .node.clickable{cursor:pointer;}#mermaid-svg-YYMhrntpUz4EgeYB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YYMhrntpUz4EgeYB .arrowheadPath{fill:#333333;}#mermaid-svg-YYMhrntpUz4EgeYB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YYMhrntpUz4EgeYB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YYMhrntpUz4EgeYB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YYMhrntpUz4EgeYB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YYMhrntpUz4EgeYB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YYMhrntpUz4EgeYB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YYMhrntpUz4EgeYB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YYMhrntpUz4EgeYB .cluster text{fill:#333;}#mermaid-svg-YYMhrntpUz4EgeYB .cluster span{color:#333;}#mermaid-svg-YYMhrntpUz4EgeYB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YYMhrntpUz4EgeYB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YYMhrntpUz4EgeYB rect.text{fill:none;stroke-width:0;}#mermaid-svg-YYMhrntpUz4EgeYB .icon-shape,#mermaid-svg-YYMhrntpUz4EgeYB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YYMhrntpUz4EgeYB .icon-shape p,#mermaid-svg-YYMhrntpUz4EgeYB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YYMhrntpUz4EgeYB .icon-shape .label rect,#mermaid-svg-YYMhrntpUz4EgeYB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YYMhrntpUz4EgeYB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YYMhrntpUz4EgeYB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YYMhrntpUz4EgeYB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} OpenCV 使用
转 OpenCV
Pillow 读取
Image.open(path)

(PIL对象, RGB格式)
np.array(image)

PIL→ndarray
cvtColor RGB→BGR

颜色通道对齐
正常保存/处理

完整代码(read_image_from_pillow.py)

python 复制代码
import cv2
import numpy as np
from PIL import Image

image_path = "../data/horse.jpg"
image = Image.open(image_path)      # PIL 读取
image.show()                        # PIL 显示

# 直接 cv2.imwrite 会报错(image 不是 ndarray)
# 解法 1:转 numpy 数组
image = np.array(image)

# 解法 2:颜色通道 RGB → BGR(PIL 是 RGB,OpenCV 是 BGR)
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

cv2.imwrite("xyz.jpg", image)       # 现在可正常保存

坑点

  • 直接 cv2.imwrite 用 PIL 对象会报错image is not a numpy array, nor a scalar
  • 原因:OpenCV 把图像当作 ndarray,PIL 返回的是 PIL 对象
  • 两步修:np.array() 转数组 + cv2.cvtColor(RGB2BGR) 对齐通道
  • 若不做 BGR 转换,保存的图颜色会变(红蓝互换)

本章小结(速查表)

操作 函数 关键点
视频→图片 os.makedirs(dir) + cv2.imwrite(name,frame) 用计数变量命名帧
图片→视频 cv2.VideoWriter(name,fourcc,fps,size) + os.listdir 尺寸必须与图片一致,先 resize
PIL→OpenCV np.array(img) + cv2.cvtColor(img, RGB2BGR) 先转数组,再对齐通道

核心易错点

  1. 视频→图片os.makedirs() 建目录,用计数变量避免覆盖
  2. 图片→视频VideoWriter 尺寸必须和图片一致,否则播放报错 (先 print(image.shape) 再 resize)
  3. PIL 图不能直接用 OpenCV :先 np.array() 转 ndarray
  4. PIL 是 RGB、OpenCV 是 BGR :不转 RGB2BGR 颜色会反转
  5. 视频相关操作结尾记得 cap.release() / out.release()
相关推荐
GlueNa2SiO340 分钟前
05-Flask表单处理与文件上传
笔记·python·学习·flask
一晌小贪欢43 分钟前
Python办公16:PDF 强力缝合——将几十个 PDF 合并为单个文档并添加页码
开发语言·python·excel·数据可视化·python办公
qq_1611112744 分钟前
深入理解Python中的Contextlib库
python·装饰器·函数·上下文管理器·contextlib
迷迭香yy1 小时前
回测过拟合检测体系从样本内外到组合稳健性评估 IG50免费开源股票数据API接口
服务器·开发语言·数据库·人工智能·python
Zane1994121 小时前
`ClassName()` 只是一步?拆开看 `__new__` 和 `__init__` 各自在干什么
开发语言·python
SEONIB_Explorer1 小时前
VEONIB 电商 UGC 视频自动化生产实战指南
人工智能·自动化·音视频·跨境电商·视频制作·veonib
GlueNa2SiO31 小时前
04-Flask数据库操作SQLAlchemy
笔记·python·flask
欧特克_Glodon1 小时前
OpenCV计算机视觉开发入门与实践<二十>:非线性变换灰度变换
c++·人工智能·opencv·计算机视觉
Data_Journal1 小时前
Playwright vs Selenium:哪个是最佳无头浏览器
开发语言·python·scrapy·microsoft·编辑器