Получение одного и того же хэша при записи и чтении видеофайла требует особого внимания.
Прежде чем сравнивать хэш, попробуйте сначала посмотреть видео.
Выполнение вашего кода дало мне следующий результат (первый кадр видео_2):
![введите здесь описание изображения](https://i.stack.imgur.com/zZOYN.png)
Когда вход (первый кадр видео):
![введите здесь описание изображения](https: //i.stack.imgur.com/49cCb.png)
Предлагаю следующие модификации:
- Используйте контейнер AVI (вместо MKV) для хранения
test_2
видео в необработанном видеоформате.
Видеоконтейнер AVI изначально предназначен для хранения необработанного видео.
Может существовать способ хранения необработанного или RGB-видео без потерь в Контейнер MKV, но я не знаю о таком варианте.
- Установите формат входных пикселей видео
test_2
.
Добавьте аргумент: pixel_format='rgb24'
.
Примечание. Я изменил его на pixel_format='bgr24'
, потому что AVI поддерживает bgr24
, а не rgb24
.
- Выберите видео кодек без потерь для
test_2
видео.
Вы можете выбрать vcodec='rawvideo'
(кодек 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:
Те же хэши получаются с помощью кодировщика ffv1 для .mkv.
- Выберите
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.
Я не смог найти способ выбрать кодек ffv1
без использования skvideo.io.FFmpegWriter
.
В реализации используется цикл 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())
person
Rotem
schedule
20.03.2021