Массив NumPy видео отличается от оригинала после записи в то же видео

У меня есть видео ( test.mkv), который я преобразовал в массив 4D NumPy - (рамка, высота, ширина, color_channel). Мне даже удалось преобразовать этот массив обратно в тот же video (), ничего не изменив. Однако, прочитав это новое, test_2.mkv, обратно в новый массив NumPy, массив первого видео отличается от массива второго видео, т.е. их хэши не совпадают и numpy.array_equal()функция возвращает false. Я пробовал использовать как python-ffmpeg, так и 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'

Я не понимаю, в чем я ошибаюсь, и в обеих соответствующих документах не указывается, как выполнить эту конкретную задачу. Любая помощь приветствуется. Спасибо.

1 ответ

Получение одного и того же хеша при записи и чтении видеофайла требует особого внимания.

Прежде чем сравнивать хеш, попробуйте сначала посмотреть видео.

Выполнение вашего кода дало мне следующий результат (первый кадр видео_2):

Когда вход (первый кадр видео):

Предлагаю следующие модификации:

  • Используйте контейнер AVI (вместо MKV) для хранения видео в формате сырого видео.
    Видеоконтейнер AVI изначально предназначен для хранения необработанного видео.
    Может быть способ хранения необработанного RGB-видео или RGB-видео без потерь в контейнере MKV, но мне неизвестен такой вариант.
  • Установите формат входных пикселей видео.
    Добавьте аргумент: pixel_format='rgb24'.
    Примечание: я изменил его на pixel_format='bgr24', потому что AVI поддерживает и нет.
  • Выберите видео кодек без потерь для 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')  # Set input pixel format.
        .output("test_2.avi", vcodec='rawvideo', r=frame_rate)  # 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 (2025, 1080, 608, 3)
print(f'{np.array_equal(video, video_2)}') # False

# Hashes do match
print(hashlib.sha256(bytes(video_2)).digest())
print(hashlib.sha256(bytes(video)).digest())

Результат (тот же хеш):

True

b'R\x11\x14\xe1e\x0bf\xc4\xde\xa5\x99\xb1\xcf\xf7\x03q\xe90k\x86\x07\xa1\x15z\xf4\xa1+\x92Z*F\xd0'

Другие вопросы по тегам