写入同一视频后,视频的 NumPy 数组与原始数组发生变化
NumPy array of a video changes from the original after writing into the same video
我有一个视频 (test.mkv
),我已将其转换为 4D NumPy 数组 - (frame, height, width, color_channel)。我什至设法将该数组转换回相同的视频 (test_2.mkv
) 而无需更改任何内容。然而,在读取这个新的 test_2.mkv
之后,返回到一个新的 NumPy 数组中,第一个视频的数组与第二个视频的数组不同,即它们的哈希值不匹配并且 numpy.array_equal()
函数 returns 错误。我尝试同时使用 python-ffmpeg and scikit-video 但无法使数组匹配。
Python-ffmpeg 尝试:
import ffmpeg
import numpy as np
import hashlib
file_name = 'test.mkv'
# Get video dimensions and framerate
probe = ffmpeg.probe(file_name)
video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
width = int(video_stream['width'])
height = int(video_stream['height'])
frame_rate = video_stream['avg_frame_rate']
# Read video into buffer
out, error = (
ffmpeg
.input(file_name, threads=120)
.output("pipe:", format='rawvideo', pix_fmt='rgb24')
.run(capture_stdout=True)
)
# Convert video buffer to array
video = (
np
.frombuffer(out, np.uint8)
.reshape([-1, height, width, 3])
)
# Convert array to buffer
video_buffer = (
np.ndarray
.flatten(video)
.tobytes()
)
# Write buffer back into a video
process = (
ffmpeg
.input('pipe:', format='rawvideo', s='{}x{}'.format(width, height))
.output("test_2.mkv", r=frame_rate)
.overwrite_output()
.run_async(pipe_stdin=True)
)
process.communicate(input=video_buffer)
# Read the newly written video
out_2, error = (
ffmpeg
.input("test_2.mkv", threads=40)
.output("pipe:", format='rawvideo', pix_fmt='rgb24')
.run(capture_stdout=True)
)
# Convert new video into array
video_2 = (
np
.frombuffer(out_2, np.uint8)
.reshape([-1, height, width, 3])
)
# Video dimesions change
print(f'{video.shape} vs {video_2.shape}') # (844, 1080, 608, 3) vs (2025, 1080, 608, 3)
print(f'{np.array_equal(video, video_2)}') # False
# Hashes don't match
print(hashlib.sha256(bytes(video_2)).digest()) # b'\x88\x00\xc8\x0ed\x84!\x01\x9e\x08 \xd0U\x9a(\x02\x0b-\xeeA\xecU\xf7\xad0xa\x9e\\xbck\xc3'
print(hashlib.sha256(bytes(video)).digest()) # b'\x9d\xc1\x07xh\x1b\x04I\xed\x906\xe57\xba\xf3\xf1k\x08\xfa\xf1\xfaM\x9a\xcf\xa9\t8\xf0\xc9\t\xa9\xb7'
Scikit 视频尝试:
import skvideo.io as sk
import numpy as np
video_data = sk.vread('test.mkv')
sk.vwrite('test_2_ski.mkv', video_data)
video_data_2 = sk.vread('test_2_ski.mkv')
# Dimensions match but...
print(video_data.shape) # (844, 1080, 608, 3)
print(video_data_2.shape) # (844, 1080, 608, 3)
# ...array elements don't
print(np.array_equal(video_data, video_data_2)) # False
# Hashes don't match either
print(hashlib.sha256(bytes(video_2)).digest()) # b'\x8b?]\x8epD:\xd9B\x14\xc7\xba\xect\x15G\xfaRP\xde\xad&EC\x15\xc3\x07\n{a[\x80'
print(hashlib.sha256(bytes(video)).digest()) # b'\x9d\xc1\x07xh\x1b\x04I\xed\x906\xe57\xba\xf3\xf1k\x08\xfa\xf1\xfaM\x9a\xcf\xa9\t8\xf0\xc9\t\xa9\xb7'
我不明白哪里出错了,而且各自的文档都没有强调如何执行此特定任务。任何帮助表示赞赏。谢谢。
写入和读取视频文件时获取相同的哈希值需要特别注意。
在比较哈希值之前,先尝试看一下视频。
执行你的代码得到了以下输出(video_2 的第一帧):
当输入(视频的第一帧)为:
我建议进行以下修改:
- 使用 AVI 容器(而不是 MKV)以原始视频格式存储
test_2
视频。
AVI 视频容器最初是为存储原始视频而设计的。
可能有一种方法可以在 MKV 容器中存储原始或无损 RGB 视频,但我不知道有这样的选项。
- 设置
test_2
视频的输入像素格式。
添加参数:pixel_format='rgb24'
.
注:我修改为pixel_format='bgr24'
,因为AVI支持bgr24
,不支持rgb24
。
- Select 视频
test_2
视频的无损编解码器。
您可能 select vcodec='rawvideo'
(AVI 支持原始视频编解码器,但 MKV 不支持)。
注:
要获得相等的哈希值,您需要寻找支持 rgb24
(或 bgr24
)像素格式的无损视频编解码器。
大多数无损编解码器,将像素格式从 RGB 转换为 YUV。
RGB 到 YUV 的转换存在舍入错误,导致哈希值不相等。
(我想有办法绕过它,但它有点复杂)。
这是您的完整代码,稍作修改:
import ffmpeg
import numpy as np
import hashlib
file_name = 'test.mkv'
# Get video dimensions and framerate
probe = ffmpeg.probe(file_name)
video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
width = int(video_stream['width'])
height = int(video_stream['height'])
frame_rate = video_stream['avg_frame_rate']
# Read video into buffer
out, error = (
ffmpeg
.input(file_name, threads=120)
.output("pipe:", format='rawvideo', pix_fmt='bgr24') # Select bgr24 instead of rgb24 (becasue raw AVI requires bgr24).
.run(capture_stdout=True)
)
# Convert video buffer to array
video = (
np
.frombuffer(out, np.uint8)
.reshape([-1, height, width, 3])
)
# Convert array to buffer
video_buffer = (
np.ndarray
.flatten(video)
.tobytes()
)
# Write buffer back into a video
process = (
ffmpeg
.input('pipe:', format='rawvideo', s='{}x{}'.format(width, height), pixel_format='bgr24', r=frame_rate) # Set input pixel format.
.output("test_2.avi", vcodec='rawvideo') # Select video code "rawvideo"
.overwrite_output()
.run_async(pipe_stdin=True)
)
process.communicate(input=video_buffer)
# Read the newly written video
out_2, error = (
ffmpeg
.input("test_2.avi", threads=40)
.output("pipe:", format='rawvideo', pix_fmt='bgr24')
.run(capture_stdout=True)
)
# Convert new video into array
video_2 = (
np
.frombuffer(out_2, np.uint8)
.reshape([-1, height, width, 3])
)
# Video dimesions change
print(f'{video.shape} vs {video_2.shape}') # (844, 1080, 608, 3) vs (844, 1080, 608, 3)
print(f'{np.array_equal(video, video_2)}') # True
# Hashes do match
print(hashlib.sha256(bytes(video_2)).digest())
print(hashlib.sha256(bytes(video)).digest())
结果(相同的散列):
True
b"\xd1yy\x97\x8e\xce\x13\xbcI#\xd2PMP\x80(i+5\xe1\xcd\xab\xf3f\xbe\xcd\xd5'\xbaq\xdd\x9b"
b"\xd1yy\x97\x8e\xce\x13\xbcI#\xd2PMP\x80(i+5\xe1\xcd\xab\xf3f\xbe\xcd\xd5'\xbaq\xdd\x9b"
更新:
使用ffv1编码器:
使用 .mkv
的 ffv1 编码器实现相同的哈希值
- Select
vcodec='ffv1'
在 output()
. 的参数中
还有一件事:
将参数 r=frame_rate
从输出参数移动到 输入 参数。
这不直观...但是在创建帧外视频时,帧速率应定义为输入的参数。
# Write buffer back into a video
process = (
ffmpeg
.input('pipe:', format='rawvideo', s='{}x{}'.format(width, height), pixel_format='rgb24', r=frame_rate) # Set input pixel format.
.output("test_2.mkv", vcodec='ffv1') # Select video code "rawvideo"
.overwrite_output()
.run_async(pipe_stdin=True)
)
这是完整的代码示例:
import ffmpeg
import numpy as np
import hashlib
file_name = 'test.mkv'
# Get video dimensions and framerate
probe = ffmpeg.probe(file_name)
video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
width = int(video_stream['width'])
height = int(video_stream['height'])
frame_rate = video_stream['avg_frame_rate']
# Read video into buffer
out, error = (
ffmpeg
.input(file_name, threads=120)
.output("pipe:", format='rawvideo', pix_fmt='rgb24') # Select rgb24 instead of rgb24 (becasue raw AVI requires rgb24).
.run(capture_stdout=True)
)
# Convert video buffer to array
video = (
np
.frombuffer(out, np.uint8)
.reshape([-1, height, width, 3])
)
# Convert array to buffer
video_buffer = (
np.ndarray
.flatten(video)
.tobytes()
)
# Write buffer back into a video
process = (
ffmpeg
.input('pipe:', format='rawvideo', s='{}x{}'.format(width, height), pixel_format='rgb24', r=frame_rate) # Set input pixel format.
.output("test_2.mkv", vcodec='ffv1') # Select video code "rawvideo"
.overwrite_output()
.run_async(pipe_stdin=True)
)
process.communicate(input=video_buffer)
# Read the newly written video
out_2, error = (
ffmpeg
.input("test_2.mkv", threads=40)
.output("pipe:", format='rawvideo', pix_fmt='rgb24')
.run(capture_stdout=True)
)
# Convert new video into array
video_2 = (
np
.frombuffer(out_2, np.uint8)
.reshape([-1, height, width, 3])
)
# Video dimesions change
print(f'{video.shape} vs {video_2.shape}') # (844, 1080, 608, 3) vs (844, 1080, 608, 3)
print(f'{np.array_equal(video, video_2)}') # True
# Hashes do match
print(hashlib.sha256(bytes(video_2)).digest())
print(hashlib.sha256(bytes(video)).digest())
结果(相同的散列,使用您的输入文件):
True
b'\x9d\xc1\x07xh\x1b\x04I\xed\x906\xe57\xba\xf3\xf1k\x08\xfa\xf1\xfaM\x9a\xcf\xa9\t8\xf0\xc9\t\xa9\xb7'
b'\x9d\xc1\x07xh\x1b\x04I\xed\x906\xe57\xba\xf3\xf1k\x08\xfa\xf1\xfaM\x9a\xcf\xa9\t8\xf0\xc9\t\xa9\xb7'
更新:
使用 Scikit-Video:
以下代码示例使用 Scikit-Video。
如果不使用 skvideo.io.FFmpegWriter
.
,我找不到 selecting ffv1
编解码器的方法
该实现使用 for 循环逐帧写入视频。
import skvideo.io as sk
import numpy as np
import hashlib
video_data = sk.vread('test.mkv')
# Create FFmpeg vidoe writer
writer = sk.FFmpegWriter('test_2_ski.mkv', outputdict={'-vcodec': 'ffv1' })
#sk.vwrite('test_2_ski.mkv', video_data)
# Write frame by frame in a loop
for i in range(video_data.shape[0]):
writer.writeFrame(video_data[i, :, :, :])
writer.close() # Close video writer.
video_data_2 = sk.vread('test_2_ski.mkv')
# Dimensions match
print(video_data.shape) # (844, 1080, 608, 3)
print(video_data_2.shape) # (844, 1080, 608, 3)
# Array elements match
print(np.array_equal(video_data, video_data_2))
# Hashes match
print(hashlib.sha256(bytes(video_data_2)).digest())
print(hashlib.sha256(bytes(video_data)).digest())
我有一个视频 (test.mkv
),我已将其转换为 4D NumPy 数组 - (frame, height, width, color_channel)。我什至设法将该数组转换回相同的视频 (test_2.mkv
) 而无需更改任何内容。然而,在读取这个新的 test_2.mkv
之后,返回到一个新的 NumPy 数组中,第一个视频的数组与第二个视频的数组不同,即它们的哈希值不匹配并且 numpy.array_equal()
函数 returns 错误。我尝试同时使用 python-ffmpeg and scikit-video 但无法使数组匹配。
Python-ffmpeg 尝试:
import ffmpeg
import numpy as np
import hashlib
file_name = 'test.mkv'
# Get video dimensions and framerate
probe = ffmpeg.probe(file_name)
video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
width = int(video_stream['width'])
height = int(video_stream['height'])
frame_rate = video_stream['avg_frame_rate']
# Read video into buffer
out, error = (
ffmpeg
.input(file_name, threads=120)
.output("pipe:", format='rawvideo', pix_fmt='rgb24')
.run(capture_stdout=True)
)
# Convert video buffer to array
video = (
np
.frombuffer(out, np.uint8)
.reshape([-1, height, width, 3])
)
# Convert array to buffer
video_buffer = (
np.ndarray
.flatten(video)
.tobytes()
)
# Write buffer back into a video
process = (
ffmpeg
.input('pipe:', format='rawvideo', s='{}x{}'.format(width, height))
.output("test_2.mkv", r=frame_rate)
.overwrite_output()
.run_async(pipe_stdin=True)
)
process.communicate(input=video_buffer)
# Read the newly written video
out_2, error = (
ffmpeg
.input("test_2.mkv", threads=40)
.output("pipe:", format='rawvideo', pix_fmt='rgb24')
.run(capture_stdout=True)
)
# Convert new video into array
video_2 = (
np
.frombuffer(out_2, np.uint8)
.reshape([-1, height, width, 3])
)
# Video dimesions change
print(f'{video.shape} vs {video_2.shape}') # (844, 1080, 608, 3) vs (2025, 1080, 608, 3)
print(f'{np.array_equal(video, video_2)}') # False
# Hashes don't match
print(hashlib.sha256(bytes(video_2)).digest()) # b'\x88\x00\xc8\x0ed\x84!\x01\x9e\x08 \xd0U\x9a(\x02\x0b-\xeeA\xecU\xf7\xad0xa\x9e\\xbck\xc3'
print(hashlib.sha256(bytes(video)).digest()) # b'\x9d\xc1\x07xh\x1b\x04I\xed\x906\xe57\xba\xf3\xf1k\x08\xfa\xf1\xfaM\x9a\xcf\xa9\t8\xf0\xc9\t\xa9\xb7'
Scikit 视频尝试:
import skvideo.io as sk
import numpy as np
video_data = sk.vread('test.mkv')
sk.vwrite('test_2_ski.mkv', video_data)
video_data_2 = sk.vread('test_2_ski.mkv')
# Dimensions match but...
print(video_data.shape) # (844, 1080, 608, 3)
print(video_data_2.shape) # (844, 1080, 608, 3)
# ...array elements don't
print(np.array_equal(video_data, video_data_2)) # False
# Hashes don't match either
print(hashlib.sha256(bytes(video_2)).digest()) # b'\x8b?]\x8epD:\xd9B\x14\xc7\xba\xect\x15G\xfaRP\xde\xad&EC\x15\xc3\x07\n{a[\x80'
print(hashlib.sha256(bytes(video)).digest()) # b'\x9d\xc1\x07xh\x1b\x04I\xed\x906\xe57\xba\xf3\xf1k\x08\xfa\xf1\xfaM\x9a\xcf\xa9\t8\xf0\xc9\t\xa9\xb7'
我不明白哪里出错了,而且各自的文档都没有强调如何执行此特定任务。任何帮助表示赞赏。谢谢。
写入和读取视频文件时获取相同的哈希值需要特别注意。
在比较哈希值之前,先尝试看一下视频。
执行你的代码得到了以下输出(video_2 的第一帧):
当输入(视频的第一帧)为:
我建议进行以下修改:
- 使用 AVI 容器(而不是 MKV)以原始视频格式存储
test_2
视频。
AVI 视频容器最初是为存储原始视频而设计的。
可能有一种方法可以在 MKV 容器中存储原始或无损 RGB 视频,但我不知道有这样的选项。 - 设置
test_2
视频的输入像素格式。
添加参数:pixel_format='rgb24'
.
注:我修改为pixel_format='bgr24'
,因为AVI支持bgr24
,不支持rgb24
。 - Select 视频
test_2
视频的无损编解码器。
您可能 selectvcodec='rawvideo'
(AVI 支持原始视频编解码器,但 MKV 不支持)。
注:
要获得相等的哈希值,您需要寻找支持 rgb24
(或 bgr24
)像素格式的无损视频编解码器。
大多数无损编解码器,将像素格式从 RGB 转换为 YUV。
RGB 到 YUV 的转换存在舍入错误,导致哈希值不相等。
(我想有办法绕过它,但它有点复杂)。
这是您的完整代码,稍作修改:
import ffmpeg
import numpy as np
import hashlib
file_name = 'test.mkv'
# Get video dimensions and framerate
probe = ffmpeg.probe(file_name)
video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
width = int(video_stream['width'])
height = int(video_stream['height'])
frame_rate = video_stream['avg_frame_rate']
# Read video into buffer
out, error = (
ffmpeg
.input(file_name, threads=120)
.output("pipe:", format='rawvideo', pix_fmt='bgr24') # Select bgr24 instead of rgb24 (becasue raw AVI requires bgr24).
.run(capture_stdout=True)
)
# Convert video buffer to array
video = (
np
.frombuffer(out, np.uint8)
.reshape([-1, height, width, 3])
)
# Convert array to buffer
video_buffer = (
np.ndarray
.flatten(video)
.tobytes()
)
# Write buffer back into a video
process = (
ffmpeg
.input('pipe:', format='rawvideo', s='{}x{}'.format(width, height), pixel_format='bgr24', r=frame_rate) # Set input pixel format.
.output("test_2.avi", vcodec='rawvideo') # Select video code "rawvideo"
.overwrite_output()
.run_async(pipe_stdin=True)
)
process.communicate(input=video_buffer)
# Read the newly written video
out_2, error = (
ffmpeg
.input("test_2.avi", threads=40)
.output("pipe:", format='rawvideo', pix_fmt='bgr24')
.run(capture_stdout=True)
)
# Convert new video into array
video_2 = (
np
.frombuffer(out_2, np.uint8)
.reshape([-1, height, width, 3])
)
# Video dimesions change
print(f'{video.shape} vs {video_2.shape}') # (844, 1080, 608, 3) vs (844, 1080, 608, 3)
print(f'{np.array_equal(video, video_2)}') # True
# Hashes do match
print(hashlib.sha256(bytes(video_2)).digest())
print(hashlib.sha256(bytes(video)).digest())
结果(相同的散列):
True
b"\xd1yy\x97\x8e\xce\x13\xbcI#\xd2PMP\x80(i+5\xe1\xcd\xab\xf3f\xbe\xcd\xd5'\xbaq\xdd\x9b"
b"\xd1yy\x97\x8e\xce\x13\xbcI#\xd2PMP\x80(i+5\xe1\xcd\xab\xf3f\xbe\xcd\xd5'\xbaq\xdd\x9b"
更新:
使用ffv1编码器:
使用 .mkv
的 ffv1 编码器实现相同的哈希值- Select
vcodec='ffv1'
在output()
. 的参数中
还有一件事:
将参数
r=frame_rate
从输出参数移动到 输入 参数。
这不直观...但是在创建帧外视频时,帧速率应定义为输入的参数。# Write buffer back into a video process = ( ffmpeg .input('pipe:', format='rawvideo', s='{}x{}'.format(width, height), pixel_format='rgb24', r=frame_rate) # Set input pixel format. .output("test_2.mkv", vcodec='ffv1') # Select video code "rawvideo" .overwrite_output() .run_async(pipe_stdin=True) )
这是完整的代码示例:
import ffmpeg
import numpy as np
import hashlib
file_name = 'test.mkv'
# Get video dimensions and framerate
probe = ffmpeg.probe(file_name)
video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
width = int(video_stream['width'])
height = int(video_stream['height'])
frame_rate = video_stream['avg_frame_rate']
# Read video into buffer
out, error = (
ffmpeg
.input(file_name, threads=120)
.output("pipe:", format='rawvideo', pix_fmt='rgb24') # Select rgb24 instead of rgb24 (becasue raw AVI requires rgb24).
.run(capture_stdout=True)
)
# Convert video buffer to array
video = (
np
.frombuffer(out, np.uint8)
.reshape([-1, height, width, 3])
)
# Convert array to buffer
video_buffer = (
np.ndarray
.flatten(video)
.tobytes()
)
# Write buffer back into a video
process = (
ffmpeg
.input('pipe:', format='rawvideo', s='{}x{}'.format(width, height), pixel_format='rgb24', r=frame_rate) # Set input pixel format.
.output("test_2.mkv", vcodec='ffv1') # Select video code "rawvideo"
.overwrite_output()
.run_async(pipe_stdin=True)
)
process.communicate(input=video_buffer)
# Read the newly written video
out_2, error = (
ffmpeg
.input("test_2.mkv", threads=40)
.output("pipe:", format='rawvideo', pix_fmt='rgb24')
.run(capture_stdout=True)
)
# Convert new video into array
video_2 = (
np
.frombuffer(out_2, np.uint8)
.reshape([-1, height, width, 3])
)
# Video dimesions change
print(f'{video.shape} vs {video_2.shape}') # (844, 1080, 608, 3) vs (844, 1080, 608, 3)
print(f'{np.array_equal(video, video_2)}') # True
# Hashes do match
print(hashlib.sha256(bytes(video_2)).digest())
print(hashlib.sha256(bytes(video)).digest())
结果(相同的散列,使用您的输入文件):
True
b'\x9d\xc1\x07xh\x1b\x04I\xed\x906\xe57\xba\xf3\xf1k\x08\xfa\xf1\xfaM\x9a\xcf\xa9\t8\xf0\xc9\t\xa9\xb7'
b'\x9d\xc1\x07xh\x1b\x04I\xed\x906\xe57\xba\xf3\xf1k\x08\xfa\xf1\xfaM\x9a\xcf\xa9\t8\xf0\xc9\t\xa9\xb7'
更新:
使用 Scikit-Video:
以下代码示例使用 Scikit-Video。
如果不使用 skvideo.io.FFmpegWriter
.
,我找不到 selecting ffv1
编解码器的方法
该实现使用 for 循环逐帧写入视频。
import skvideo.io as sk
import numpy as np
import hashlib
video_data = sk.vread('test.mkv')
# Create FFmpeg vidoe writer
writer = sk.FFmpegWriter('test_2_ski.mkv', outputdict={'-vcodec': 'ffv1' })
#sk.vwrite('test_2_ski.mkv', video_data)
# Write frame by frame in a loop
for i in range(video_data.shape[0]):
writer.writeFrame(video_data[i, :, :, :])
writer.close() # Close video writer.
video_data_2 = sk.vread('test_2_ski.mkv')
# Dimensions match
print(video_data.shape) # (844, 1080, 608, 3)
print(video_data_2.shape) # (844, 1080, 608, 3)
# Array elements match
print(np.array_equal(video_data, video_data_2))
# Hashes match
print(hashlib.sha256(bytes(video_data_2)).digest())
print(hashlib.sha256(bytes(video_data)).digest())