簡體   English   中英

將 16 位灰度 PNG 轉換為 HEVC/x265

[英]Convert 16bit Grayscale PNG to HEVC/x265

我想將 12 位圖像信號轉換為 HEVC 以進行有效壓縮。 因為我需要能夠重建原始的 12 位信號,所以壓縮需要是無損可逆的。 目前我的數據是 16 位 PNG 文件。

我的第一次嘗試是使用 ffmpeg:

ffmpeg -y -framerate 1 -i input.png -c:v libx265 -x265-params "lossless=1" output.mp4

不幸的是,output 是不可逆的。 從 mp4 中提取圖像時,像素值略有偏差。

ffmpeg -i output.mp4 -vframes 1 reconstructed.png

以下答案建議首先將輸入轉換為 YUV444 以避免 ffmpeg 的意外行為: 無損 x264 壓縮

到目前為止,我未能成功地將我的 16 位文件轉換為 YUV,將其轉換為 x256 並在解碼時接收到正確的重建。

有沒有直接的方法將 16 位圖像轉換為 HEVC?

我找到了一個帶有輕微舍入錯誤的解決方案:

編碼:

  • 基於以下帖子: 如何將 png 渲染為 h.265 12 位視頻?
    使用可以使用以下編解碼器參數: -x265-params lossless=1 -pix_fmt yuv444p12le用於有損 12 bpc 編碼。

  • 通過反復試驗,我意識到 12 位數據必須在每個 16 位元素的高 12 位中。 您需要將輸入像素放大 16 以將數據放在高位。
    (縮放 16 相當於將 uint16 元素左移 4)。
    要放大像素,您可以使用colorlevels視頻過濾器:
    -vf colorlevels=rimax=0.0625:gimax=0.0625:bimax=0.0625

以下命令對單個幀進行編碼:

 ffmpeg -i input.png -vf colorlevels=rimax=0.0625:gimax=0.0625:bimax=0.0625 -c:v libx265 -x265-params lossless=1 -pix_fmt yuv444p12le output.mkv

解碼:

  • 對於解碼,您需要將像素除以 16 以將數據放在低 12 位中。
    (除以 16 相當於將 uint16 元素右移 4)。
    我找不到使用colorlevels的解決方案,所以我使用了curves過濾器:
    -vf "curves=r='0/0 1.0/0.0625':g='0/0 1.0/0.0625':b='0/0 1.0/0.0625'"
  • 16 位 PNG 的合適像素格式是rgb48be

以下命令解碼單個幀(並除以 16):

ffmpeg -i output.mkv -vf "curves=r='0/0 1.0/0.0625':g='0/0 1.0/0.0625':b='0/0 1.0/0.0625'" -pix_fmt rgb48be reconstructed.png

差異:
input.pngreconstructed.png之間的最大絕對差為4級。
差異的原因可能是由於將 RGB 轉換為 YUV 並返回而導致的舍入誤差。


我使用以下 MATLAB 代碼進行測試:

I = imread('peppers.png');

% Build 10 PNG images (used as input).
for i = 1:10
    J = insertText(I, [size(I,2)/2-18, size(I,1)/2-36], num2str(i), 'FontSize', 72);
    J = imnoise(im2double(J), 'gaussian', 0, 0.01); % Add some noise
    J = uint16(round(J*4095)); % Convert to 12 bits range (range [0, 4095])
    imwrite(J, sprintf('input%02d.png', i), 'fmt', 'png', 'BitDepth', 16, 'Mode', 'lossless'); % Write to PNG file
end

 %Encode video file using x265 codec, and 12 bits YUV444 format. 
[status, cmdout] = system('ffmpeg -y -i input%02d.png -vf colorlevels=rimax=0.0625:gimax=0.0625:bimax=0.0625 -c:v libx265 -x265-params lossless=1 -pix_fmt yuv444p12le output.mkv');
if (status ~= 0), disp(cmdout);end

% Decode output.mkv into 10 PNG image files
[status, cmdout] = system('ffmpeg -y -i output.mkv -vf "curves=r=''0/0 1.0/0.0625'':g=''0/0 1.0/0.0625'':b=''0/0 1.0/0.0625''" -pix_fmt rgb48be reconstructed%02d.png');
if (status ~= 0), disp(cmdout);end

% Compare input and output:
for i = 1:10
    I = imread(sprintf('input%02d.png', i));
    J = imread(sprintf('reconstructed%02d.png', i));
    max_abs_diff = max(max(max(imabsdiff(I, J))));
    disp(['max_abs_diff = ', num2str(max_abs_diff)]);
end

更新:

使用灰度格式:
工作灰度時,不需要將像素格式轉換為 YUV。
從灰度轉換到 YUV444 會使輸入數據的大小乘以 3,所以最好避免轉換。

以下命令對單個灰度幀進行編碼:

 ffmpeg -i input.png -vf "curves=all='0/0 0.0625/1.0'" -c:v libx265 -x265-params lossless=1 -pix_fmt gray12le -bsf:v hevc_metadata=video_full_range_flag=1 output.mkv

以下命令解碼單個灰度幀(並除以 16):

ffmpeg -i output.mkv -vf "curves=all='0/0 1.0/0.0625'" -pix_fmt gray16be reconstructed.png

最大絕對差為 2。


關於使用-bsf:v hevc_metadata=video_full_range_flag=1注意事項:

在 H.265 中,Y 顏色通道的默認范圍是“有限范圍”。
對於 8 位,“有限范圍”適用 [16, 235]。
對於 12 位,“有限范圍”適用 [256, 3760]。
當使用“全范圍”[0, 255] 為 8 位或 [0, 4095] 為 12 位時,您需要在流的元數據中指定它。
使用 FFmpeg 設置元數據的方法是使用比特流過濾器

我試圖為灰度 10 位數據實現相同的目標。

感謝ffmpeg-user 郵件列表上的 Paul B Mahol,我已經能夠通過使用臨時 rawvideo 文件並欺騙 rawvideo 解復用器以我想要的位深度解釋文件來解決剩余的舍入錯誤。

我假設相同的解決方案適用於 12 位數據,並且可以擴展到 RGB 數據。 ffmpeg 命令行可以在我的相關(幾乎重復)問題中找到: https://stackoverflow.com/a/69874453/17261462

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM