質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

1805閲覧

Pngファイルの保存した結果がpython(opencv-python)とC++(libpng)で異なる

Y...M

総合スコア18

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

0クリップ

投稿2022/10/20 00:48

編集2022/10/20 01:19

前提

opencv-pythonで①Png画像を読み込み、②ピクセル出力した結果および③保存したPng画像と、
C++ とlibpngで①Png画像を読み込み、④ピクセル出力した結果および⑤保存したPng画像では、
②と④はすべて同じ結果になりますが、③と⑤をバイナリで比較するとヘッダーは同じでしたが、IDATのデータ部分が異なる結果になりました。

環境:
python 3.10
opencv-python 4.6.0.66
C++17
libpng 1.6.37

Png画像:
ビットの深さ 32

実現したいこと

上記③と⑤を同じ結果にしたい。
opencvもlibpngを使用しているため、同じ結果になると思いましたが、
Pngの圧縮などの知識があまりなく、なぜ異なるのかがわかっていません。

該当のソースコード

python

1cvimg = cv2.imread("./temp/sample.png", -1) 2cv2.imwrite("./temp/cv.png", cvimg, [cv2.IMWRITE_PNG_COMPRESSION, 6])

opencv内のlibpng使用箇所

C++

1bool PngEncoder::write( const Mat& img, const std::vector<int>& params ) 2{ 3 png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 ); 4 png_infop info_ptr = 0; 5 FILE * volatile f = 0; 6 int y, width = img.cols, height = img.rows; 7 int depth = img.depth(), channels = img.channels(); 8 volatile bool result = false; 9 AutoBuffer<uchar*> buffer; 10 11 if( depth != CV_8U && depth != CV_16U ) 12 return false; 13 14 if( png_ptr ) 15 { 16 info_ptr = png_create_info_struct( png_ptr ); 17 18 if( info_ptr ) 19 { 20 if( setjmp( png_jmpbuf ( png_ptr ) ) == 0 ) 21 { 22 if( m_buf ) 23 { 24 png_set_write_fn(png_ptr, this, 25 (png_rw_ptr)writeDataToBuf, (png_flush_ptr)flushBuf); 26 } 27 else 28 { 29 f = fopen( m_filename.c_str(), "wb" ); 30 if( f ) 31 png_init_io( png_ptr, (png_FILE_p)f ); 32 } 33 34 int compression_level = -1; // Invalid value to allow setting 0-9 as valid 35 int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy 36 bool isBilevel = false; 37 38 for( size_t i = 0; i < params.size(); i += 2 ) 39 { 40 if( params[i] == IMWRITE_PNG_COMPRESSION ) 41 { 42 compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy 43 compression_level = params[i+1]; 44 compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION); 45 } 46 if( params[i] == IMWRITE_PNG_STRATEGY ) 47 { 48 compression_strategy = params[i+1]; 49 compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED); 50 } 51 if( params[i] == IMWRITE_PNG_BILEVEL ) 52 { 53 isBilevel = params[i+1] != 0; 54 } 55 } 56 57 if( m_buf || f ) 58 { 59 if( compression_level >= 0 ) 60 { 61 png_set_compression_level( png_ptr, compression_level ); 62 } 63 else 64 { 65 // tune parameters for speed 66 // (see http://wiki.linuxquestions.org/wiki/Libpng) 67 png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB); 68 png_set_compression_level(png_ptr, Z_BEST_SPEED); 69 } 70 png_set_compression_strategy(png_ptr, compression_strategy); 71 72 png_set_IHDR( png_ptr, info_ptr, width, height, depth == CV_8U ? isBilevel?1:8 : 16, 73 channels == 1 ? PNG_COLOR_TYPE_GRAY : 74 channels == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, 75 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, 76 PNG_FILTER_TYPE_DEFAULT ); 77 78 png_write_info( png_ptr, info_ptr ); 79 80 if (isBilevel) 81 png_set_packing(png_ptr); 82 83 png_set_bgr( png_ptr ); 84 if( !isBigEndian() ) 85 png_set_swap( png_ptr ); 86 87 buffer.allocate(height); 88 for( y = 0; y < height; y++ ) 89 buffer[y] = img.data + y*img.step; 90 91 png_write_image( png_ptr, buffer.data() ); 92 png_write_end( png_ptr, info_ptr ); 93 94 result = true; 95 } 96 } 97 } 98 }

⑤ 読み込み省略してます。

C++

1void test(png_bytepp image, 2 int width, 3 int height, 4 int bitDpth, 5 int colorType, 6 int interlaceType) 7{ 8 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 9 png_infop info = png_create_info_struct(png); 10 11 if ((info == NULL) || (setjmp(png_jmpbuf(png)) != 0)) { 12 png_destroy_write_struct(&png, NULL); 13 return; 14 } 15 FILE* f = fopen("cpp.png", "wb"); 16 png_init_io(png, (png_FILE_p)f); 17 18 png_set_compression_level(png, 6); 19 png_set_IHDR(png, info,width, height, bitDpth, colorType, 0,PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 20 21 png_write_info(png, info); 22 png_write_image(png, image); 23 png_write_end(png, info); 24 25 png_destroy_write_struct(&png, &info); 26 fclose(f); 27}

③と⑤バイナリ比較抜粋
イメージ説明

melian😄を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

int32_t

2022/10/20 01:06

IHDR の内容は1ビットも違いがなかったですか? IDAT のサイズは同一ですか?
Y...M

2022/10/20 01:22

質問のほうに比較した結果の抜粋を貼り付けました。 IHDRは同じと思います。 IDATのサイズはIDATの手前4バイトのことを指されている場合は同じと思います。 Pngファイルのプロパティ上のサイズおよびIDAT以降のデータ量は異なっています。
maisumakun

2022/10/20 01:30

> 上記③と⑤を同じ結果にしたい。 なぜ同じ結果にしたいのでしょうか?
Y...M

2022/10/20 01:43

>maisumakunさん 質問に記載の2つの方法で同じ画像を出力したいと考えており、 同じことを証明する際に、バイナリレベルで同じであれば、 出力画像が同じであることを証明できると思ったからです。
jbpb0

2022/10/20 08:21

c++でもopencvで保存したら、ファイルのデータが③と同じになりそうな気がしますが、c++ではlibpngを使わないといけない理由があるのでしょうか? また、③と⑤をpythonのopencvで読み込んだ時のnumpy配列のデータは、異なるのでしょうか?
yominet

2022/10/20 14:53

可逆圧縮のはずなのでこだわる理由がちょっと不明なのだけど OpenCVのソース部分をコピーして、void test2()を作成し、 各pnt_set_***関数に引き渡される関数、結果を自分のtest()と 比較・確認してみてはいかがでしょうか?
Y...M

2022/10/20 23:39

>yominetさん Png画像の同一性はピクセルが同じなら同じ画像という理解でも良いんですかね? 「可逆圧縮のはずなのでこだわる理由がちょっと不明なのだけど」について こだわりはあまりなく、異なる手段で出力したPng画像が同じ画像なこと確認したいのですが、 どのように確認すれば同じといえるかご存じでしょうか?
jbpb0

2022/10/21 00:31 編集

> 異なる手段で出力したPng画像が同じ画像なこと確認したいのですが、 どのように確認すれば同じといえるかご存じでしょうか? それを確認するために、 > ③と⑤をpythonのopencvで読み込んだ時のnumpy配列のデータは、異なるのでしょうか? と書きました 圧縮された状態(③と⑤)のデータが違っても、圧縮を解いた状態(③と⑤をpythonのopencvで読み込んだ時のnumpy配列)のデータが同じなら、「実質」同じではないか、と思いまして
Y...M

2022/10/21 00:37

>jbpb0さん ③と⑤をpythonで読み込み、ピクセル値をテキストに吐き出したものは同じ結果になりました。
jbpb0

2022/10/21 01:07 編集

> 質問に記載の2つの方法で同じ画像を出力したい の「同じ画像」の意味によると思います 質問文に書いてあるようにファイルの内容に相違点がある場合は、もちろん「同じ画像ファイル」ではありませんが、画像ファイルをプログラムで読み込んだ状態の配列(グレースケールなら2次元、カラーならば3次元)のデータが全部同じなら「同じ画像」だと私は思いました 「同じ画像」の意味をそのように解釈して、上のコメントを書きました 質問者さんが、「同じ画像」を「同じ画像ファイル」の意味で使ってるのでしたら、私のコメントは的外れです 質問者さんの目的は、何を「同じ」にしたいのでしょうか? 質問文に書いてあるように、「画像ファイル」の内容を全く「同じ」にしたいのでしょうか? それとも、画像ファイルを「プログラムで読み込んだ状態のデータ」が「同じ」であればいいのでしょうか? > ③と⑤をpythonで読み込み、ピクセル値をテキストに吐き出したものは同じ結果になりました。 から、後者の意味では「同じ」です
guest

回答3

0

自己解決

以下のようにすることで③と⑤が同じになりました。
STRATEGYの設定が異なっていることが原因でした。

C++側でpng_set_compression_strategy()を設定しない場合は、
DEFAULTではなく、FILTEREDになっているようでした。
opencv側は設定しなければDEFAULTにになります。

python

1cvimg = cv2.imread("./temp/sample.png", -1) 2cv2.cvtColor(cvimg, cv2.COLOR_BGR2RGB) 3cvparam = [ 4 cv2.IMWRITE_PNG_COMPRESSION, 5 6, 6 cv2.IMWRITE_PNG_STRATEGY, 7 cv2.IMWRITE_PNG_STRATEGY_FILTERED, 8] 9cv2.imwrite( 10 "./temp/cv.png", 11 cvimg, 12 cvparam, 13)

投稿2022/10/20 23:44

Y...M

総合スコア18

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

c++

1int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy

OpenCV の方は圧縮ストラテジとしてデフォルトで RLE を指定しているようです。C++コードの方は png_set_compression_strategy() が呼ばれていないのでデフォルトの Z_DEFAULT が使われているのでしょう。これを合わせてみてはどうでしょうか。

OpenCV では png_set_bgr() も使われていますね。

投稿2022/10/20 01:54

編集2022/10/20 01:58
int32_t

総合スコア20884

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

Pngの圧縮などの知識があまりなく、なぜ異なるのかがわかっていません。

1ピクセルも変わらない画像でも、

  • 行ごとに施すフィルター
  • zlibによる圧縮処理の調整

により、PNGファイルにしたときのファイル内容は変わりえます。

投稿2022/10/20 01:43

maisumakun

総合スコア145184

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2022/10/20 01:46

> 同じことを証明する際に、バイナリレベルで同じであれば、 出力画像が同じであることを証明できると思ったからです。 基本的に、全く同じPNG生成ルーチンを全く同じ設定で動かさない限り、同一バイナリは望めません。
Y...M

2022/10/20 01:49

ありがとうございます。 >行ごとに施すフィルター このフィルターはIHDRで指定するフィルターとは異なるものでしょうか? >zlibによる圧縮処理の調整 同じバージョンのlibpngを使用している場合でも上記が異なり得るということでしょうか?
maisumakun

2022/10/20 02:02

> このフィルターはIHDRで指定するフィルターとは異なるものでしょうか? はい、行ごとに設定するもので、IDATの中身にも含まれます。
Y...M

2022/10/20 02:11

そうだったんですね・・・。 行ごとのフィルターがどうなっているかがわかれば同じ処理にできる可能性があるということですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問