コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2024年11月25日 >>> 2024年11月12日
link もっと後

2024年11月25日

libjpeg-turboのライブラリは2つある

目次: Linux

高速なJPEGデコード/エンコードライブラリで有名なlibjpeg-turboですが、ライブラリというかAPIが2系統あります(公式ドキュメントへのリンクlibjpeg-turboのソースコード)。系統その1はlibjpeg(IJG: Independent JPEG Groupのライブラリ)互換のAPIで、系統その2は独自のTurboJPEG APIです。パッケージの名前もややこしくて、しばらくしたら忘れてしまいそうなのでメモしておきます。

libjpeg系
ライブラリlibjpeg.so、ヘッダjpeglib.h、パッケージlibjpeg-turbo8、API名はjpeg_から始まります。
TurboJPEG系
ライブラリlibturbojpeg.so、ヘッダturbojpeg.h、パッケージlibturbojpeg、API名はtj(バージョン2.x系)もしくはtj3(バージョン3.x系)から始まります。

各パッケージに含まれるファイルも示しておきます。

Ubuntu 22.04 AArch64向け各パッケージのファイル一覧
libjpeg-turbo8

/usr/lib/aarch64-linux-gnu/libjpeg.so.8
/usr/lib/aarch64-linux-gnu/libjpeg.so.8.2.2

libjpeg-turbo8-dev

/usr/include/aarch64-linux-gnu/jconfig.h
/usr/include/jerror.h
/usr/include/jmorecfg.h
/usr/include/jpegint.h
/usr/include/jpeglib.h
/usr/lib/aarch64-linux-gnu/libjpeg.a
/usr/lib/aarch64-linux-gnu/libjpeg.so
/usr/lib/aarch64-linux-gnu/pkgconfig/libjpeg.pc


libturbojpeg

/usr/lib/aarch64-linux-gnu/libturbojpeg.so.0
/usr/lib/aarch64-linux-gnu/libturbojpeg.so.0.2.0

libturbojpeg0-dev

/usr/include/turbojpeg.h
/usr/lib/aarch64-linux-gnu/libturbojpeg.a
/usr/lib/aarch64-linux-gnu/libturbojpeg.so
/usr/lib/aarch64-linux-gnu/pkgconfig/libturbojpeg.pc

例としてUbuntu 22.04 AArch64向けのものを示しましたが、他のアーキテクチャやバージョンでも構造は同じです。

編集者:すずき(2024/12/09 00:30)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年11月22日

nvJPEGとNVJPGとJetson APIその3 - nvJPEG encode API

目次: Linux

半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。NVIDIA GPUとCUDAを使ってJPEGが扱えるそうで、API名はnvJPEGだそうです(nvJPEGのAPIドキュメント)。

nvJPEG encoding

前回まではデコード処理でした。今回はエンコードのAPIをご紹介します。decoupled decodingのような複雑なAPIは存在しないようです。

Raw YUV420の画素データを置くメモリを確保する際はcudaMalloc()ではなく、cudaMallocHost()を使う点がデコードと異なります。エンコードはこんな感じでした。

nvJPEG encodingのAPI呼び出し順

	cudaStream_t stream = nullptr;
	nvjpegHandle_t nvj_handle = nullptr;
	nvjpegEncoderState_t nvj_state = nullptr;
	nvjpegEncoderParams_t nvj_param = nullptr;
	nvjpegImage_t inbuf = {0};
	uint8_t *jpegbuf = nullptr;
	size_t bufsize = 0;
	int r;

	// Create
	cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking);

	nvjpegCreateEx(NVJPEG_BACKEND_DEFAULT, nullptr, nullptr, NVJPEG_FLAGS_DEFAULT, &nvj_handle);
	nvjpegEncoderStateCreate(nvj_handle, &nvj_state, stream);
	nvjpegEncoderParamsCreate(nvj_handle, &nvj_param, stream);

	// Set JPEG parameters
	nvjpegEncoderParamsSetQuality(nvj_param, 80, stream);
	nvjpegEncoderParamsSetSamplingFactors(nvj_param, NVJPEG_CSS_420, stream);


	// 2のべき乗境界に切り上げる
#define ALIGN_2N(a, b)    (((a) + (b) - 1) & ~((b) - 1))

	inbuf.pitch[0] = ALIGN_2N(width, 256);
	inbuf.pitch[1] = ALIGN_2N(width, 256);
	inbuf.pitch[2] = ALIGN_2N(width, 256);
	cudaMallocHost((void **)&inbuf.channel[0], inbuf.pitch[0] * height);
	cudaMallocHost((void **)&inbuf.channel[1], inbuf.pitch[1] * height);
	cudaMallocHost((void **)&inbuf.channel[2], inbuf.pitch[2] * height);

	bufsize = width * height * 3 / 2;
	jpegbuf = (uint8_t *)malloc(bufsize);

	// YUVデータをinbufにロードする
	// 詳細はソースコードを参照

	// Encoding
	nvjpegChromaSubsampling_t jpegsamp = NVJPEG_CSS_420;
	size_t jpegsize = bufsize;

	nvjpegEncodeYUV(nvj_handle, nvj_state, nvj_param, &inbuf, jpegsamp, width, height, stream);
	nvjpegEncodeRetrieveBitstream(nvj_handle, nvj_state, jpegbuf, &jpegsize, stream);

	cudaStreamSynchronize(stream);

	// Destroy
	free(jpegbuf);

	cudaFree(inbuf.channel[0]);
	cudaFree(inbuf.channel[1]);
	cudaFree(inbuf.channel[2]);

	nvjpegEncoderParamsDestroy(nvj_param);
	nvjpegEncoderStateDestroy(nvj_state);
	nvjpegDestroy(nvj_handle);

	cudaStreamDestroy(stream);

YUV420Pのロード部分は本質と関係ないことと、若干長いので省略しました。nvJPEG APIの数は少なくてシンプルです。nvJPEGやNVJPGは機械学習用データ(JPEGファイルのことが多いらしい)を高速に入力するため?らしく、デコード命なのでしょう。エンコードもできるけど主眼ではないから、APIもシンプルなものしかないのかな?

実行

前回同様にソースコードを置いておきます。

使い方はコードの先頭にコメントで書いている通りですが、ここでも説明しておきます。引数はありません。ファイル名test_420.yuvのRaw YUV420ファイルを読み込んで、ファイル名simple_420.jpgのJPEGファイルを書き出します。

コンパイル、結果確認
$ g++ -g -O2 -Wall 20241120_nvjpeg_simple_enc.cpp -lnvjpeg -lcudart
$ ./a.out

$ ffplay -i simple_420.jpg

エンコード結果はJPEGです。ffplayでも普段お使いの画像ビューアでも、何を使って確認しても構いません。

編集者:すずき(2024/12/08 15:24)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年11月20日

nvJPEGとNVJPGとJetson APIその2 - nvJPEG simple decode API

目次: Linux

半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。NVIDIA GPUとCUDAを使ってJPEGが扱えるそうで、API名はnvJPEGだそうです(nvJPEGのAPIドキュメント)。

nvJPEG simple decoding

前回ご紹介したdecoupled decodingは呼び出すべきAPI数が多くて、ウワァ……と引いてしまう見た目でした。今回のsimple decodingはその名の通りシンプルです。ちなみにエンコード側もあります。なぜかsimpleに該当するAPIしかなく、decoupled相当のエンコード用APIは存在しないようです。変なの。

Simple decodingはこんな感じでした。Decoupledと比べるとかなりAPIが少なく済みます。

nvJPEG simple decodingのAPI呼び出し順

	cudaStream_t stream = nullptr;
	nvjpegHandle_t nvj_handle = nullptr;
	nvjpegJpegState_t nvj_state = nullptr;
	nvjpegImage_t outbuf = {0};
	uint8_t *img_buf[4] = {nullptr};
	int img_stride[4] = {0};
	int img_sz[4] = {0};
	int r;

	// Create
	cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking);

	nvjpegCreateEx(NVJPEG_BACKEND_DEFAULT, nullptr, nullptr, NVJPEG_FLAGS_DEFAULT, &nvj_handle);
	nvjpegJpegStateCreate(nvj_handle, &nvj_state);

	//2のべき乗境界に切り上げる
#define ALIGN_2N(a, b)    (((a) + (b) - 1) & ~((b) - 1))

	outbuf.pitch[0] = ALIGN_2N(width, 256);
	outbuf.pitch[1] = ALIGN_2N(width, 256);
	outbuf.pitch[2] = ALIGN_2N(width, 256);
	cudaMalloc(&outbuf.channel[0], outbuf.pitch[0] * height);
	cudaMalloc(&outbuf.channel[1], outbuf.pitch[1] * height);
	cudaMalloc(&outbuf.channel[2], outbuf.pitch[2] * height);

	img_stride[0] = ALIGN_2N(width, 256);
	img_stride[1] = ALIGN_2N(width, 256);
	img_stride[2] = ALIGN_2N(width, 256);
	img_buf[0] = (uint8_t *)malloc(img_stride[0] * height);
	img_buf[1] = (uint8_t *)malloc(img_stride[1] * height);
	img_buf[2] = (uint8_t *)malloc(img_stride[2] * height);

	//Decoupled phase decoding
	nvjpegGetImageInfo(nvj_handle, jpegbuf, jpegsize, &jpegcomps, &jpegsamp, jpegwidths, jpegheights);
	nvjpegDecode(nvj_handle, nvj_state, jpegbuf, jpegsize, NVJPEG_OUTPUT_YUV, &outbuf, stream);

	cudaStreamSynchronize(stream);

	for (int i = 0; i < 3; i++) {
		cudaMemcpy2D(img_buf[i], img_stride[i], outbuf.channel[i], outbuf.pitch[i],
			width, height, cudaMemcpyDeviceToHost);
	}


	// Destroy
	free(img_buf[0]);
	free(img_buf[1]);
	free(img_buf[2]);

	cudaFree(outbuf.channel[0]);
	cudaFree(outbuf.channel[1]);
	cudaFree(outbuf.channel[2]);

	nvjpegJpegStateDestroy(nvj_state);
	nvjpegDestroy(nvj_handle);

	cudaStreamDestroy(stream);

1枚だけJPEGをデコードするならこちらの方が断然楽ですね。

実行

前回同様にソースコードを置いておきます。

使い方はコードの先頭にコメントで書いている通りですが、ここでも説明しておきます。引数はありません。ファイル名test_420.jpgのJPEGファイルを読み込んで、ファイル名simple_420.yuvのRawvideoファイルを書き出します。

コンパイル、結果確認
$ g++ -g -O2 -Wall 20241120_nvjpeg_simple_dec.cpp -lnvjpeg -lcudart
$ ./a.out

$ ffplay -f rawvideo -video_size 1920x1440 -pixel_format yuv420p -i simple_420.yuv

デコード結果のRawvideoを確認するときはffplayを使うと便利です。

編集者:すずき(2024/12/06 23:34)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年11月18日

nvJPEGとNVJPGとJetson APIその1 - nvJPEG decoupled API

目次: Linux

半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。NVIDIA GPUとCUDAを使ってJPEGが扱えるそうで、API名はnvJPEGだそうです(nvJPEGのAPIドキュメント)。それと別にJPEGのHWコーデックもあり、名前はNVJPG(Eがない)です。nvJPEGと紛らわしくて仕方ありません。

nvJPEG decoupled decoding

NVIDIAがnvJPEGのサンプルを公開しています(nvJPEGデコードサンプルコード)。ありがたいですね。でもなぜかサンプルはデコーダーしかありません。一応Resizeサンプルでエンコーダーを扱っていますが、なぜこんなサンプルの構造にしたのでしょう。

エンコード方法は公式ドキュメント(nvJPEGのドキュメント)の3.1.5 JPEG Encoding Exampleがシンプルで見やすいかもしれません。こちらはなぜかデコーダーのサンプルがありません。変なの。

困ったことにデコーダーのサンプルはRGBからYUVに変更すると動きません。試行錯誤したところストライドが間違っているようです。あとYUV420P(UとVプレーンの幅と高さはYプレーンの半分)なのに、YとUVが同じ高さじゃないとお気に召さないようでした。すなわち、

  • ストライドを256バイトの倍数にする
  • YUVの3プレーン全ての高さを同じにする

このようにするとデコードできました。ドキュメントに何も書いていないので、バグか合っているか全くわかりません。上記を考慮しつつDecoupled decodingする場合のAPI呼び出し順を載せておきます。

CUDA関連の謎APIについては、CUDA Stream Management(cudaStream_tなどのドキュメント)と、CUDA Memory Management(cudaMalloc()などのドキュメント)をご参照ください。

nvJPEG decoupled decodingのAPI呼び出し順

	cudaStream_t stream = nullptr;
	nvjpegHandle_t nvj_handle = nullptr;
	nvjpegJpegState_t nvj_dcstate = nullptr;
	nvjpegBufferPinned_t pinned_buffers[2] = {nullptr};
	nvjpegBufferDevice_t device_buffer = nullptr;
	nvjpegJpegStream_t jpeg_streams[2] = {nullptr};
	nvjpegDecodeParams_t nvj_decparams = nullptr;
	nvjpegJpegDecoder_t nvj_dec = nullptr;
	nvjpegImage_t outbuf = {0};
	uint8_t *img_buf[4] = {nullptr};
	int img_stride[4] = {0};
	int img_sz[4] = {0};
	int r;

	// Create
	cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking);

	nvjpegCreateEx(NVJPEG_BACKEND_DEFAULT, nullptr, nullptr, NVJPEG_FLAGS_DEFAULT, &nvj_handle);
	nvjpegDecoderCreate(nvj_handle, NVJPEG_BACKEND_DEFAULT, &nvj_dec);
	nvjpegDecoderStateCreate(nvj_handle, nvj_dec, &nvj_dcstate);

	nvjpegBufferPinnedCreate(nvj_handle, nullptr, &pinned_buffers[0]);
	nvjpegBufferPinnedCreate(nvj_handle, nullptr, &pinned_buffers[1]);
	nvjpegBufferDeviceCreate(nvj_handle, nullptr, &device_buffer);

	nvjpegJpegStreamCreate(nvj_handle, &jpeg_streams[0]);
	nvjpegJpegStreamCreate(nvj_handle, &jpeg_streams[1]);

	nvjpegDecodeParamsCreate(nvj_handle, &nvj_decparams);

	//2のべき乗境界に切り上げる
#define ALIGN_2N(a, b)    (((a) + (b) - 1) & ~((b) - 1))

	outbuf.pitch[0] = ALIGN_2N(width, 256);
	outbuf.pitch[1] = ALIGN_2N(width, 256);
	outbuf.pitch[2] = ALIGN_2N(width, 256);
	cudaMalloc(&outbuf.channel[0], outbuf.pitch[0] * height);
	cudaMalloc(&outbuf.channel[1], outbuf.pitch[1] * height);
	cudaMalloc(&outbuf.channel[2], outbuf.pitch[2] * height);

	img_stride[0] = width;
	img_stride[1] = width / 2;
	img_stride[2] = width / 2;
	img_sz[0] = img_stride[0] * height;
	img_sz[1] = img_stride[1] * height / 2;
	img_sz[2] = img_stride[2] * height / 2;
	img_buf[0] = (uint8_t *)malloc(img_sz[0]);
	img_buf[1] = (uint8_t *)malloc(img_sz[1]);
	img_buf[2] = (uint8_t *)malloc(img_sz[2]);

	//Decoupled phase decoding
	nvjpegStateAttachDeviceBuffer(nvj_dcstate, device_buffer);

	nvjpegOutputFormat_t fmt = NVJPEG_OUTPUT_YUV;
	nvjpegDecodeParamsSetOutputFormat(nvj_decparams, fmt);

	int index = 0;
	nvjpegJpegStreamParse(nvj_handle, jpegbuf, jpegsize, 0, 0, jpeg_streams[index]);
	nvjpegStateAttachPinnedBuffer(nvj_dcstate, pinned_buffers[index]);
	nvjpegDecodeJpegHost(nvj_handle, nvj_dec, nvj_dcstate, nvj_decparams, jpeg_streams[index]);

	nvjpegDecodeJpegTransferToDevice(nvj_handle, nvj_dec, nvj_dcstate, jpeg_streams[index], stream);
	nvjpegDecodeJpegDevice(nvj_handle, nvj_dec, nvj_dcstate, &outbuf, stream);

	cudaStreamSynchronize(stream);

	for (int i = 0; i < 3; i++) {
		cudaMemcpy2D(img_buf[i], img_stride[i], outbuf.channel[i], outbuf.pitch[i],
			(i == 0) ? width : width / 2,
			(i == 0) ? height : height / 2,
			cudaMemcpyDeviceToHost);
	}


	// Destroy
	free(img_buf[0]);
	free(img_buf[1]);
	free(img_buf[2]);

	cudaFree(outbuf.channel[0]);
	cudaFree(outbuf.channel[1]);
	cudaFree(outbuf.channel[2]);

	nvjpegDecodeParamsDestroy(nvj_decparams);

	nvjpegJpegStreamDestroy(jpeg_streams[0]);
	nvjpegJpegStreamDestroy(jpeg_streams[1]);

	nvjpegBufferPinnedDestroy(pinned_buffers[0]);
	nvjpegBufferPinnedDestroy(pinned_buffers[1]);
	nvjpegBufferDeviceDestroy(device_buffer);

	nvjpegJpegStateDestroy(nvj_dcstate);
	nvjpegDecoderDestroy(nvj_dec);
	nvjpegDestroy(nvj_handle);

	cudaStreamDestroy(stream);

今回紹介したdecoupled decodingは速度が稼げるみたいですが、複雑です。もっと簡単なsimple decodingもあるので次回にご紹介しようと思います。

実行

ソースコードも置いておきます。

使い方はコードの先頭にコメントで書いている通りですが、ここでも説明しておきます。引数はありません。ファイル名test_420.jpgのJPEGファイルを読み込んで、ファイル名decoupled_420.yuvのRawvideoファイルを書き出します。

コンパイル、結果確認
$ g++ -g -O2 -Wall 20241118_nvjpeg_decoupled.cpp -lnvjpeg -lcudart
$ ./a.out

$ ffplay -f rawvideo -video_size 1920x1440 -pixel_format yuv420p -i decoupled_420.yuv

Rawvideoを確認するときはffplayを使うと便利です。FFMPEGは本当にありがたい。

編集者:すずき(2024/12/08 23:40)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年11月17日

JTSA Limited大会参加2024

目次: 射的

JTSA Limitedの大会に参加しました。去年はベレッタが壊れましたが、今年は大丈夫でした。記録は絶好調というほどではありませんでしたが、自己ベストに近い71.65秒のタイムが出ました(総合79位/115人、LM 16位/26人)。さすがに3年目ともなると大会本番のまぐれ当たり&自己ベスト、なんて嬉しいアクシデントは発生しませんでした。


JTSA Limited練習会の記録

大会の記録だけ見ると、2022年85秒、2023年76秒、2024年71秒と順調に記録は伸びています。良きかな良きかな。来年はどうなるかな?

編集者:すずき(2024/11/26 15:40)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2024年11月25日 >>> 2024年11月12日
link もっと後

管理用メニュー

link 記事を新規作成

<2024>
<<<11>>>
-----12
3456789
10111213141516
17181920212223
24252627282930

最近のコメント5件

  • link 21年9月20日
    すずきさん (11/19 01:04)
    「It was my pleasure.」
  • link 21年9月20日
    whtさん (11/17 23:41)
    「This blog solves my ...」
  • link 24年10月1日
    すずきさん (10/06 03:41)
    「xrdpで十分動作しているので、Wayl...」
  • link 24年10月1日
    hdkさん (10/03 19:05)
    「GNOMEをお使いでしたら今はWayla...」
  • link 24年10月1日
    すずきさん (10/03 10:12)
    「私は逆にVNCサーバーに繋ぐ使い方をした...」

最近の記事3件

  • link 24年11月25日
    すずき (12/09 00:30)
    「[libjpeg-turboのライブラリは2つある] 目次: Linux高速なJPEGデコード/エンコードライブラリで有名なl...」
  • link 24年12月6日
    すずき (12/09 00:08)
    「[YUVのrawvideo画像を生成、表示する方法] 目次: LinuxFFMPEG(ffmpeg)を使ってYUV420やYU...」
  • link 24年11月18日
    すずき (12/08 23:40)
    「[nvJPEGとNVJPGとJetson APIその1 - nvJPEG decoupled API] 目次: Linux半年...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 12/09 00:30