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

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

新規登録して質問してみよう
ただいま回答率
85.50%
OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Q&A

解決済

3回答

25554閲覧

byte配列のcv::Matへの画像変換

kamingout

総合スコア44

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

0グッド

1クリップ

投稿2016/03/03 22:52

byte配列に格納している画像データをMatに画像配列として保存します。この時、cv::imdecodeを使用しています。その後に画素値の変換をかけてcv::imencodeで再び画像データをbyte配列に戻そうとしたところ圧縮の影響で元々のbyte配列の画像データと異なる配列数になってしまいます。
imencode、imdecodeをかませても配列数を保ったままbyteの画像変換はできないでしょうか?

よろしくお願いいたします。

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

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

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

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

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

yohhoy

2016/03/04 09:14

変換前データ(byte配列に格納している画像データ)と、変換後データ(もしくはimencodeの第一引数extの値)は、それぞれどんな形式を指定していますか?
kamingout

2016/03/04 09:19

変換前の画像は今の所指定はしておらず(pngやjpgを使うことが多い)、変換後の画像の形式はjpgで吐き出すようにしています。 画像形式やimdecode.imencodeのパラメータをいじることでbyte配列数を保ったままの変換ができますかね?
yohhoy

2016/03/04 13:06

byte配列数というのはbyte要素の数という意味でしょうか?例えば1000byte入力データから、きっきり同サイズの1000byteの出力データを得たい?
kamingout

2016/03/04 13:18

そうです。配列の要素の数です。入力が1000byteのデータだとしたら出力も1000byteで得たいです。 なぜならポインタを使用できない環境のため、確保する配列数は予め分かっている必要があるからです。 お手数おかけいたしますがよろしくおねがいします。
guest

回答3

0

かなり遅い返信ですが、最近一部似たような状態に遭遇したので返信します。
私の場合は、生画像またはimencode()で圧縮した画像を、ソケット通信で送信→受信して、受信したBYTE配列(char *型)をMat型に復元したい。です。
ちょっとずれた回答かもしれませんが、どなたかの参考になれば幸い。
まず、PNG形式であれば可逆圧縮なので、imencode()前と、imdecode()後は同じになるはずです。

そしてBYTE配列から画像を取り出す件について。(申し訳ないのですが、いま手元に開発環境ないので、下記のコードは非検証です。方向性はあっているはずなので、ミスあれば指摘頂けると幸い。)

・圧縮されたBYTE配列があり、それをMat型にしたい場合
imdecode()の戻値がMat型なので復元できます。(ただしBYTE配列の長さはimencode()した時に決まるので、imencode()によりリサイズされたバッファの.size()を取得し、そのサイズを別途知る必要があります。)以下のような感じ。

C++

1// 受信側 2char *cBuf; // ←この先に受信した圧縮されたBYTE配列が既に入っているものとする 3int size = 10240; // ←このサイズは別途分かっているものとする 4vector<uchar> buf; 5buf.assign(cBuf, cBuf + size); 6cv::Mat m = cv::imdecode(cv::Mat(buf), -1);

・非圧縮の生画像のBYTE配列があり、それをMat型にしたい場合
imdecode()は使えないので、なんとかMat型のdatastart~dataendの領域にデータを入れてやればいいのですが、直接代入するとおかしなことになります。なので、単純にMat型の引数付きコンストラクタを使います。以下のような感じ。

C++

1// 受信側 2char *cBuf; // ←この先に受信した非圧縮BYTE配列が既に入っているものとする 3int rows = 480; // ←このサイズは別途分かっているものとする 4int cols = 640; // ←このサイズは別途分かっているものとする 5cv::Mat m(rows, cols, CV_8UC3, (void *)cBuf); // 透過ありだとCV_8UC4?

投稿2018/09/19 15:12

ttb

総合スコア67

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

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

kamingout

2018/12/18 13:51

このような実装の方法もあるのですね。 参考になりました。 ありがとうございました。
guest

0

ベストアンサー

imencode、imdecodeをかませても配列数を保ったままbyteの画像変換はできないでしょうか?
変換前の画像は今の所指定はしておらず(pngやjpgを使うことが多い)、変換後の画像の形式はjpgで吐き出すようにしています。
入力が1000byteのデータだとしたら出力も1000byteで得たいです。

実際問題として実現不可能です。若干条件を緩和して、入力データサイズ>出力データサイズ ならば、偶然上手くいくこともありますが、実効上は全くお勧めしません。

入出力データとして想定されているPNGやJPEGといったデータ形式は、一定のアルゴリズムにより「圧縮」されています。このような変換が行われたデータでは、圧縮された状態のデータサイズと、展開したオリジナルのデータサイズにほとんど相関がありません。

PNGとJPEGでは根底にある考え方(lossless vs. lossy)が大きく異なるのですが、JPEGだけをとってみても、Nバイトのデータをデコード(imdecode)して映像を加工、エンコード(imencode)した後の出力バイト数を制御することは現実的には不可能です。JPEGの場合はかなり古い規格ということもあり、その仕様上も出力バイト数を細かく制御できません(荒い制御は可能です)。また、データサイズを小さくすればするほど、画像としては汚く(劣化)します。

投稿2016/03/04 14:17

yohhoy

総合スコア6189

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

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

kamingout

2016/03/07 14:02

返信遅くなってしまい申し訳ありません。 細かいアドバイスどうもありがとうございます! 現実的にbyteデータをデコード、エンコードした場合はサイズの復元は不可能なのですね。。。 現在、C#のコードでbyteデータをC++で実装したDLLに送り、DLL上でデコード、エンコードしたデータを再びC#のプログラムに送るような処理を行っています。 このような処理の場合、C#でデータを受け取る場合はどのように受け取ることができるでしょうか? ちなみに、C#ではポインタ系の処理はしないようにしております。 お手数おかけしますが、ご返信よろしくお願いいたします。
yohhoy

2016/03/07 15:54 編集

C#ではポインタ系の処理はしないよう=C#コードでIntPtr構造体を使わないという意味ですか? C++のcv::imencode()関数は出力バッファとしてstd::vector<uchar>&を取りますから、1)素直にuchar*を返してC#側はIntPtrとして扱う、2)C#側で大きめのbyte[]を確保してそこに転送するしかないと思います。 もしくは https://github.com/shimat/opencvsharp などのC#ラッパーライブラリを利用するなど。
guest

0

こんにちは。

記述ミスかも知れませんが、生画像をデコードできないですよ。生画像をエンコードしてjpegやpngへ変換します。jpegやpngをデコードして生画像を出来るだけ回復します。


【追記】

そのjpgをデコードして生画像をエンコード前と同じbyte配列数で復元したいのですが無理ですかね?

私はimencode, imdecodeを使ったことはないのでずか、復元する画像のサイズやピクセル情報のバイト数を元の画像と同じになるよう指定すればできる筈ですよ。

ここにサンプルがありました。
jpegに変換して戻す部分は下記ですね。

C++

1 Mat src = imread("lenna.png"); 2 3 //(1) jpeg compression 4 vector<uchar> buff;//buffer for coding 5 vector<int> param = vector<int>(2); 6 param[0]=CV_IMWRITE_JPEG_QUALITY; 7 param[1]=95;//default(95) 0-100 8 9 imencode(".jpg",src,buff,param); 10 cout<<"coded file size(jpg)"<<buff.size()<<endl;//fit buff size automatically. 11 Mat jpegimage = imdecode(Mat(buff),CV_LOAD_IMAGE_COLOR);

このsrcとjpegimageの画像サイズや色深度が異なってしまうと言うことでしょうか?

サンプルを見る限り画像サイズの指定はないようですね。
ピクセル情報のバイト数は、CV_LOAD_IMAGE_COLORの部分で指定するようです。
imread()と同じ指定をすれば同じに出来るはずです。imread()でのデフォルトはCV_LOAD_IMAGE_COLORです。

投稿2016/03/04 00:52

編集2016/03/04 09:44
Chironian

総合スコア23272

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

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

kamingout

2016/03/04 09:16

記述ミスしてました! byte配列に格納している生画像をエンコードしてjpgにします。そのjpgをデコードして生画像をエンコード前と同じbyte配列数で復元したいのですが無理ですかね?
Chironian

2016/03/04 09:38

本文中に追記します。
kamingout

2016/03/04 13:15

ありがとうございます。 imdecodeでimreadと同じ指定をすることでうまくいくのですね! ただ、画像の配列をimreadではなく、元々与えられた状態で持っています。 この場合はimdecodeではbyte配列数を元々の画像のbyte配列と同じにしたい場合はどのような指定をしたらできるのでしょう? 関数を見た感じ、設定する値はopencvのマクロを設定することしかできないのではないかと思いました。 お手数おかけいたしますがよろしくおねがいします。
Chironian

2016/03/04 13:42

> 画像の配列をimreadではなく、元々与えられた状態で持っています。 > この場合はimdecodeではbyte配列数を元々の画像のbyte配列と同じにしたい場合は > どのような指定をしたらできるのでしょう? 「元々与えられた状態で持ってる画像」はどのフォーマットなのでしょうか? そのフォーマットをcv:imdecodeがサポートしていれば画像サイズと色深度は復元できると思います。(jpegは非可逆ですので画像そのものは多少変化します。) http://opencv.jp/opencv-2.1/cpp/reading_and_writing_images_and_video.html に一応記載があるようです。 エンコードに成功しているのであれば、戻せそうな気がするのですが。 試しに元々の画像をimwriteでjpegファイルへ出力してみてはどうでしょう? それを表示してみればエンコードできているかどうか確認できると思います。 エンコードに失敗しているようでしたら、戻せる可能性はないと思います。
Chironian

2016/03/04 14:34

yohhoyさんの回答をみて混乱してきました。 kamingoutさんがやりたことは、下記のどちらですか? ①私の理解 元々の画像=生画像(非圧縮)をcv::encodeで一度jpg画像へ変換する。 そして、その画像をcv::decodeで元々の画像と同じメモリサイズの画像へ戻す。 ②yohhoyさんの回答から思い当たった別解釈 元々の画像=png or jpg画像をcv::decodeで一度生画像へ変換する。 そして、その生画像をcv::encodeでjpgへ圧縮し、元々の画像と同じメモリサイズの画像へ戻す。 もし、②であればyohhoyさんの回答の通りです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問