目次: Linux
半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。Jetson特有のAPI群がありまして、API名はJetson Linux API(のなかのMultimedia APIs)だそうです(Jetson Linux APIドキュメント)。
今回はエンコードのAPIをご紹介します。APIの使い方は簡単です。こんな感じでした。
NvJPEGEncoder *jpgenc = nullptr;
NvBuffer *inbuf = nullptr;
uint8_t *buffer = nullptr;
size_t bufsize = 0;
int r;
// Create
jpgenc = NvJPEGEncoder::createJPEGEncoder("jpgenc");
// Align
#define ALIGN_2N(a, b) (((a) + (b) - 1) & ~((b) - 1))
NvBuffer::NvBufferPlaneFormat fmts[3];
fmts[0].width = width;
fmts[0].height = height;
fmts[0].bytesperpixel = 1;
fmts[0].stride = ALIGN_2N(width, 256);
fmts[0].sizeimage = fmts[0].stride * fmts[0].height;
fmts[1].width = width / 2;
fmts[1].height = height / 2;
fmts[1].bytesperpixel = 1;
fmts[1].stride = ALIGN_2N(width / 2, 256);
fmts[1].sizeimage = fmts[1].stride * fmts[1].height;
fmts[2].width = width / 2;
fmts[2].height = height / 2;
fmts[2].bytesperpixel = 1;
fmts[2].stride = ALIGN_2N(width / 2, 256);
fmts[2].sizeimage = fmts[2].stride * fmts[2].height;
inbuf = new NvBuffer(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, V4L2_MEMORY_USERPTR, 3, fmts, 0);
if (!inbuf) {
printf("error in %s:%d\n", __func__, __LINE__);
return -1;
}
r = inbuf->allocateMemory();
if (r) {
printf("error in %s:%d\n", __func__, __LINE__);
return -1;
}
bufsize = width * height * 3 / 2;
buffer = (uint8_t *)malloc(bufsize);
// Encoding
uint8_t *jpegbuf = buffer;
size_t jpegsize = bufsize;
int quality = 80;
r = jpgenc->encodeFromBuffer(*inbuf, JCS_YCbCr, &jpegbuf, jpegsize, quality);
// Destroy
free(buffer);
delete inbuf;
delete jpgenc;
若干NvBufferの確保がややこしいです(参考: NvBufferPlaneのドキュメント、その隣のNvBufferPlaneFormatも参考になります)けど、基本的にはencodeFromBuffer()を呼ぶだけです。
ソースコードを置いておきます。
使い方はコードの先頭にコメントで書いている通りですが、ここでも説明しておきます。引数はありません。ファイル名test_420.yuvのRaw YUV420ファイルを読み込んで、ファイル名jetson_420.jpgのJPEGファイルを書き出します。
$ g++ -g -O2 -g -Wall \ -I jetson_multimedia_api/include/ \ -I jetson_multimedia_api/include/libjpeg-8b/ \ jetson_multimedia_api/samples/common/classes/NvJpegDecoder.cpp \ jetson_multimedia_api/samples/common/classes/NvJpegEncoder.cpp \ jetson_multimedia_api/samples/common/classes/NvBuffer.cpp \ jetson_multimedia_api/samples/common/classes/NvElement.cpp \ jetson_multimedia_api/samples/common/classes/NvElementProfiler.cpp \ jetson_multimedia_api/samples/common/classes/NvLogging.cpp \ jetson_enc.cpp \ -L /usr/lib/aarch64-linux-gnu/nvidia/ \ -lnvjpeg $ ./a.out $ ffplay -i jetson_420.jpg
エンコード結果はJPEGです。ffplayでも普段お使いの画像ビューアでも、何を使って確認しても構いません。
目次: Linux
半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。Jetson特有のAPI群がありまして、API名はJetson Linux API(のなかのMultimedia APIs)だそうです(Jetson Linux APIドキュメント)。
今回はデコードのAPIをご紹介します。APIの使い方は簡単でこんな感じでした。
NvJPEGDecoder *jpgdec = nullptr;
NvBuffer *outbuf = nullptr;
int r;
// Create
jpgdec = NvJPEGDecoder::createJPEGDecoder("jpgdec");
// Decoding
uint32_t jpeg_pixfmt;
uint32_t jpeg_width;
uint32_t jpeg_height;
r = jpgdec->decodeToBuffer(&outbuf, jpegbuf, jpegsize, &jpeg_pixfmt, &jpeg_width, &jpeg_height);
// Destroy
delete outbuf;
delete jpgdec;
注意すべき点は2つあります。1つ目はdecodeToBuffer()が勝手にoutbufを確保して返してくるので、delete outbufしなければならないことです。Turbo JPEGと異なり、予めバッファを確保しておけばメモリ確保処理を回避するような仕組みはなさそうでした。イマイチです。
2つ目はProgressive JPEGがデコードできないことです。デコードしようとするとdecodeToBuffer()でハングします。どんなJPEGファイルが来るかわからない状況で使う場合、JPEGファイルの中身をチェックしてBaseline JPEGはハードウェアデコード、Progressive JPEGはソフトウェアデコードする仕組みが必要です。
しかし前に話したとおりJetson APIの裏にいるlibnvjpeg.soが謎にIJG JPEGのAPIを実装しているため、本家IJG JPEGのlibjpeg.soがリンクできません。この状態でどうやってProgressive JPEGをソフトウェアデコードすれば良いのでしょう。Jetson APIの裏にいるlibnvjpeg.soをIJG JPEGだと思って呼べば動作するんでしょうか?
ソースコードを置いておきます。
使い方はコードの先頭にコメントで書いている通りですが、ここでも説明しておきます。引数はありません。ファイル名test_420.yuvのRaw YUV420ファイルを読み込んで、ファイル名jetson_420.jpgのJPEGファイルを書き出します。
$ g++ -g -O2 -g -Wall \ -I jetson_multimedia_api/include/ \ -I jetson_multimedia_api/include/libjpeg-8b/ \ jetson_multimedia_api/samples/common/classes/NvJpegDecoder.cpp \ jetson_multimedia_api/samples/common/classes/NvJpegEncoder.cpp \ jetson_multimedia_api/samples/common/classes/NvBuffer.cpp \ jetson_multimedia_api/samples/common/classes/NvElement.cpp \ jetson_multimedia_api/samples/common/classes/NvElementProfiler.cpp \ jetson_multimedia_api/samples/common/classes/NvLogging.cpp \ jetson_dec.cpp \ -L /usr/lib/aarch64-linux-gnu/nvidia/ \ -lnvjpeg $ ./a.out $ ffplay -f rawvideo -video_size 1920x1440 -pixel_format yuv420p -i jetson_420.yuv
デコード結果のRawvideoを確認するときはffplayを使うと便利です。
目次: Linux
半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。Jetson特有のAPI群がありまして、API名はJetson Linux API(のなかのMultimedia APIs)だそうです(Jetson Linux APIドキュメント)。
Jetsonを持っていないと使えません。最新機種はJetson AGX OrinかJetson Orin Nanoですが、AGX Orinは40万円、Orin Nanoは10万円近くするボッタクリ価格です。Xavierまではそこそこ安かったのにねえ……。
Jetson Linux APIがもしシステムにインストールされていなければapt-get install nvidia-l4t-jetson-multimedia-apiで使えるようになるはずです。Jetson用のパッケージは一覧があります(一覧へのリンク)ので、手動でダウンロードしたいときにはありがたいですね。
Jetson Linux APIは実装も使い方もかなり独特というか……はっきり言って変です。
変な点その1、APIの提供方法です。API実装は/usr/src/jetson_multimedia_api/samples/common/classesにソースコードで置かれています。ライセンスはBSDライセンスです。なぜ*.soや*.aで提供しないのでしょう?
後ほど紹介するJPEGデコード、エンコードのコンパイルにはNvJpegDecoder.cppとNvJpegEncoder.cppとお供のツール類が必要ですから、あえてディレクトリ名を省かずに書くと、下記のようなコマンドでコンパイルできます。面倒ならばjetson_multimedia_api/samples/common/classes/*.cppで良いです、NVIDIA提供のサンプルもワイルドカードを使っていました。
g++ -O2 -g -Wall \ -I jetson_multimedia_api/include/ \ -I jetson_multimedia_api/include/libjpeg-8b/ \ jetson_multimedia_api/samples/common/classes/NvJpegDecoder.cpp \ jetson_multimedia_api/samples/common/classes/NvJpegEncoder.cpp \ jetson_multimedia_api/samples/common/classes/NvBuffer.cpp \ jetson_multimedia_api/samples/common/classes/NvElement.cpp \ jetson_multimedia_api/samples/common/classes/NvElementProfiler.cpp \ jetson_multimedia_api/samples/common/classes/NvLogging.cpp \ (自分のソースコード) \ -L /usr/lib/aarch64-linux-gnu/nvidia/ \ -lnvjpeg
変な点その2、JPEG機能のヘッダ名です。jetson_multimedia_api/include/NvJpegDecoder.hを見ると、JPEG関連の機能はjetson_multimedia_api/include/libjpeg-8b/jpeglib.hにあるjpeglib.hヘッダにて定義されています。そう、このヘッダ名はIJG JPEGライブラリのヘッダと同じ名前です。なぜそんな場所にヘッダを置くのか?IJG JPEGと互換性がない(APIが増えている)のになぜ同じ名前?
変な点その3、JPEG機能のライブラリ名です。jpeglib.hだからlibjpeg.soかと思うじゃないですか、それは罠でリンクすべきはlibnvjpeg.soです。良くないことにlibjpeg.soも存在していて、さらに良くないことにデコード機能だけ使っている場合は間違ってlibjpeg.soを指定してもリンクできてしまい、
$ ./a.out JPEG parameter struct mismatch: library thinks size is 656, caller expects 776
実行時にこのようなエラーが出るだけでうんともすんとも動きません。IJG JPEGと同じAPI名にするならTurbo JPEGのようにlibjpeg.soの方も置き換えれば問題は起きなかったのに、なぜ中途半端な実装に?
変な点その4、Jetson APIとNVJPEGの衝突です。以前紹介したNVJPEGはJetsonでも動きますがインストールはオススメしないです。
この2つが両方ともインストールされ、同じ-lnvjpegでリンクしてもライブラリパスによってリンクエラーになったり実行時のダイナミックリンクエラーになります。全く違う機能とAPI体系なのに、なぜ同じライブラリ名になっているの?
JPEGデコードの話は次にしたいと思います。Jetsonは適当なのか突貫なのか知りませんが、すっごい変だしちょっと使っただけなのに無限に疑問が出てきます。みんな良くこんなの使ってるなあ……。
< | 2024 | > | ||||
<< | < | 12 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | - | - | - | - |
合計:
本日: