TL;DR
- JPEG画像の圧縮・展開は「人間が知覚できない程度のズレ」を許容するアルゴル。
- 繰り返し保存したとき、確かに数値上は劣化しているようには見える。ただ実害はない(はず)。
- 正直、画像データが判読不可能なくらいまで壊れる、という説明はちょっと怪しい。
書いたのは誰?
- 15年以上組み込み屋さんだったのに、今年頭に左遷。非常に悲しい。画像処理ちょとわかる。
はじめに
人類は、何故に 「JPEG形式で繰り返し保存すると画質が劣化するという都市伝説」 を信じるのか…
都市伝説を放置しておくのは、えんじにゃーんとして悲しい。ちゃんと検証してみよう。
なお、実は結論は既にほかの方が繰り返しJPEG圧縮と画質
原画像の空間周波数性質の影響という論文を出されている。
環境
テストコード
a.out : main.cpp
g++ main.cpp -o a.out \
-I /usr/local/include/opencv4 \
-lopencv_core \
-lopencv_quality \
-lopencv_imgcodecs
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <vector>
#include <opencv2/quality/qualityssim.hpp>
int main(void)
{
cv::Mat origImg = cv::imread("test.jpg", cv::IMREAD_COLOR );
cv::Mat img = origImg.clone();
for(int i = 0 ; i < 20 ; i ++ )
{
std::vector<uchar> buf;
cv::Mat privImg = img.clone();
cv::imencode(".jpg", img, buf);
img = cv::imdecode(buf, cv::IMREAD_COLOR );
cv::Scalar retOrig = cv::quality::QualitySSIM::compute(img, origImg, cv::noArray());
cv::Scalar retPriv = cv::quality::QualitySSIM::compute(img, privImg, cv::noArray());
std::cout << "i = " << i;
std::cout << " <ORIG>";
std::cout << " SSIM=" << retOrig ;
std::cout << " <PRIV>";
std::cout << " SSIM=" << retPriv ;
std::cout << std::endl;
}
return 0;
}
実験結果
SSIMは1.0であれば「全く同じ画像」で、0.0になるほどずれている画像になる。
kmtr@kmtr-VMware-Virtual-Platform:~/work/build4-main/jpegtest$ ./a.out
i = 0 <ORIG> SSIM=[0.995108, 0.997485, 0.996675, 0] <PRIV> SSIM=[0.995108, 0.997485, 0.996675, 0]
i = 1 <ORIG> SSIM=[0.994834, 0.997316, 0.996453, 0] <PRIV> SSIM=[0.999379, 0.999584, 0.999496, 0]
i = 2 <ORIG> SSIM=[0.994745, 0.997275, 0.996401, 0] <PRIV> SSIM=[0.999813, 0.999895, 0.999876, 0]
i = 3 <ORIG> SSIM=[0.994701, 0.997261, 0.996382, 0] <PRIV> SSIM=[0.999916, 0.999967, 0.999958, 0]
i = 4 <ORIG> SSIM=[0.994672, 0.997253, 0.99637, 0] <PRIV> SSIM=[0.99995, 0.999984, 0.999981, 0]
i = 5 <ORIG> SSIM=[0.994652, 0.997249, 0.996364, 0] <PRIV> SSIM=[0.999965, 0.99999, 0.999989, 0]
i = 6 <ORIG> SSIM=[0.994639, 0.997245, 0.99636, 0] <PRIV> SSIM=[0.999977, 0.999994, 0.999994, 0]
i = 7 <ORIG> SSIM=[0.994627, 0.997243, 0.996358, 0] <PRIV> SSIM=[0.999983, 0.999996, 0.999996, 0]
i = 8 <ORIG> SSIM=[0.994619, 0.997241, 0.996355, 0] <PRIV> SSIM=[0.999988, 0.999997, 0.999997, 0]
i = 9 <ORIG> SSIM=[0.994614, 0.997239, 0.996354, 0] <PRIV> SSIM=[0.99999, 0.999997, 0.999998, 0]
i = 10 <ORIG> SSIM=[0.99461, 0.997238, 0.996353, 0] <PRIV> SSIM=[0.999993, 0.999998, 0.999999, 0]
i = 11 <ORIG> SSIM=[0.994606, 0.997237, 0.996352, 0] <PRIV> SSIM=[0.999995, 0.999999, 0.999999, 0]
i = 12 <ORIG> SSIM=[0.994603, 0.997236, 0.996352, 0] <PRIV> SSIM=[0.999995, 0.999999, 0.999999, 0]
i = 13 <ORIG> SSIM=[0.9946, 0.997236, 0.996351, 0] <PRIV> SSIM=[0.999997, 0.999999, 1, 0]
i = 14 <ORIG> SSIM=[0.994598, 0.997235, 0.996351, 0] <PRIV> SSIM=[0.999997, 0.999999, 1, 0]
i = 15 <ORIG> SSIM=[0.994596, 0.997235, 0.996351, 0] <PRIV> SSIM=[0.999998, 0.999999, 1, 0]
i = 16 <ORIG> SSIM=[0.994595, 0.997235, 0.99635, 0] <PRIV> SSIM=[0.999999, 1, 1, 0]
i = 17 <ORIG> SSIM=[0.994594, 0.997235, 0.99635, 0] <PRIV> SSIM=[0.999999, 1, 1, 0]
i = 18 <ORIG> SSIM=[0.994595, 0.997235, 0.99635, 0] <PRIV> SSIM=[0.999999, 1, 1, 0]
i = 19 <ORIG> SSIM=[0.994594, 0.997235, 0.99635, 0] <PRIV> SSIM=[1, 1, 1, 0]
考察
本当に画像は劣化しているのか?
右側のPRIV(前回)との比較結果を見れば、ほぼほぼ誤差が収束するように見えるだろう。
左側のORIG(原画像)との比較結果だけを見ると、こういう結論も言える。
「JPEG形式で繰り返し保存すると画質が劣化しているじゃないか!!」
だが、ちょっと待ってほしい。SSIM=0.005程度の差分は人間が知覚できるのだろうか?
それでは、実際のサンプル画像を見てみよう。卵の右側の半透明な白身部分(麩との境界)あたりなどに若干の差異が生じている。ただ、人間が知覚できない程度と言える。
元画像
0回目画像
例えば20回上書き保存をしたらこのような画像になる。
19回目画像
そうなると、こういう反論も考えられる。
実際に目に見えて数値違ってますよね?詭弁じゃないんですか?
ごめんね... 不可逆圧縮/展開はそもそもその「詭弁」ベースでの圧縮方式 なんだ。
どういうことかというと、あるJPEGデータがあっても、それを展開するときにどんな計算処理をするのかにyとて、展開結果の画像は異なってしまう、というのがある。例えば、「整数で計算」「浮動小数で計算」といった方法でさえも、最終的な出力画像は異なる。
その中で、人間が知覚できない程度の誤差は存在しても「無視」せざるを得ない。
私は外から取ってきたJPEG画像を手元で圧縮したら劣化した、どういうことだ?
これは単純な話で、JPEGファイルに含まれていた各種情報を、次回の圧縮に活用できていなかったからと思われる。
今回のテストでは、まったく同じパラメータで何度も繰り返し圧縮をしている。つまり、量子化処理を繰り返せば最終的には同じ結果にしかならない。
ところが、外部で作ったJPEG画像を、手元で圧縮するときと異なるパラメータになっている場合、前よりも悪い条件を使って圧縮する場合もある 。
例えば、ダウンロードしたときは圧縮率95%だったのに、手元で展開した後に、85%で圧縮したら、そりゃまあ画像は劣化しますよね、と。
まとめ
- JPEG画像の圧縮・展開は「人間が知覚できない程度のズレ」を許容するアルゴル。
- 繰り返し保存したとき、確かに数値上は劣化しているようには見える。ただ実害はない(はず)。
- 正直、画像データが判読不可能なくらいまで壊れる、という説明はちょっと怪しい。