目次: Linux
半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。libjpeg-turboを使ってJPEGのデコードとエンコードを行う方法を紹介します(公式ドキュメントへのリンク)。
以前(2024年11月25日の日記参照)紹介したようにlibjpeg-turboは2.x系と3.x系の2系統のAPIがありまして、今回は2.x系APIの使い方を紹介します。DebianやUbuntuのaptパッケージだと2.x系が使われているので特に困らないと思いますが、最新版をビルドして使う場合は3.x系APIになるのでご注意ください。
デコードと対になるような作りです。libjpeg-turboはとても簡単でありがたい。
tj_handle = tjInitCompress();
// Rawvideo
img_stride[0] = width;
img_stride[1] = width / 2;
img_stride[2] = width / 2;
img_buf[0] = (uint8_t *)malloc(img_stride[0] * height);
img_buf[1] = (uint8_t *)malloc(img_stride[1] * height / 2);
img_buf[2] = (uint8_t *)malloc(img_stride[2] * height / 2);
// Encoding
tjCompressFromYUVPlanes(tj_handle, (const uint8_t **)img_buf, width, img_stride, height, subsamp, &jpegbuf, &jpegsize, quality, 0);
// Destroy
free(img_buf[0]);
free(img_buf[1]);
free(img_buf[2]);
tjDestroy(tj_handle);
デコード同様にinitしてエンコードするだけです。デコードと違う点が1つあって、エンコードではJPEGを格納するバッファ(上記だとjpegbuf)のサイズが不明である問題があります。解決方法は2つあります。連続でエンコードする場合など、メモリの確保解放を減らしたい場合は1番目が良いでしょう。1枚だけエンコードするだけならどちらでも構いません。
やってはいけないのが1番目だけどバッファサイズが不足することです。エンコード関数が勝手にバッファmalloc()してポインタを返してきて、元々確保していたバッファが行方不明になってしまいます。メモリリークするので注意が必要です。
私のコードは1番目の方法をとっていて、バッファサイズはYUV420つまり非圧縮時のサイズです。非圧縮時のサイズを上回ることはほぼないらしいのであまり問題ないはず。気になる人はlibjpeg-turboのAPIにJPEGサイズの最大値を返すtjBufSize()があるので、APIが返す値でjpegbufを確保するのが正しいお作法です。
ソースコードを置いておきます。
使い方はコードの先頭にコメントで書いている通りですが、ここでも説明しておきます。引数はありません。ファイル名test_420.yuvのRaw YUV420ファイルを読み込んで、ファイル名turbo2_420.jpgのJPEGファイルを書き出します。
$ g++ -g -O2 -Wall 20241205_turbo2_enc.cpp -lturbojpeg $ ./a.out $ ffplay -i turbo2_420.jpg
エンコード結果はJPEGです。ffplayでも普段お使いの画像ビューアでも、何を使って確認しても構いません。
この記事にコメントする
目次: Linux
半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。libjpeg-turboを使ってJPEGのデコードとエンコードを行う方法を紹介します(公式ドキュメントへのリンク)。
以前(2024年11月25日の日記参照)紹介したようにlibjpeg-turboは2.x系と3.x系の2系統のAPIがありまして、今回は2.x系APIの使い方を紹介します。DebianやUbuntuのaptパッケージだと2.x系が使われているので特に困らないと思いますが、最新版をビルドして使う場合は3.x系APIになるのでご注意ください。
前に使ったnvJPEGは訳のわからん儀式が必要でしたが、libjpeg-turboはとても簡単です。
// Create
tj_handle = tjInitDecompress();
// Rawvideo
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]);
// Decoding
tjDecompressToYUVPlanes(tj_handle, jpegbuf, jpegsize, img_buf, width, img_stride, height, 0);
// Destroy
free(img_buf[0]);
free(img_buf[1]);
free(img_buf[2]);
tjDestroy(tj_handle);
基本的にはinitしてデコードするだけです。
ソースコードを置いておきます。
使い方はコードの先頭にコメントで書いている通りですが、ここでも説明しておきます。引数はありません。ファイル名test_420.jpgのJPEGファイルを読み込んで、ファイル名turbo2_420.yuvのRawvideoファイルを書き出します。
$ g++ -g -O2 -Wall 20241204_turbo2_dec.cpp -lturbojpeg $ ./a.out $ ffplay -f rawvideo -video_size 1920x1440 -pixel_format yuv420p -i turbo2_420.yuv
デコード結果のRawvideoを確認するときはffplayを使うと便利です。
この記事にコメントする
目次: Linux
Debian Testingのapt-get updateをしたら、xrdp経由で起動しているX.orgがエラーで起動しなくなりました。こんなエラーが出ています。
[ 1416.016] (EE)
[ 1416.016] (EE) Backtrace:
[ 1416.016] (EE) 0: /usr/lib/xorg/Xorg (OsLookupColor+0x14d) [0x556fb13c8dad]
[ 1416.017] (EE) 1: /lib/x86_64-linux-gnu/libc.so.6 (__sigaction+0x40) [0x7f084f9dad20]
[ 1416.017] (EE) 2: /lib/x86_64-linux-gnu/libc.so.6 (pthread_key_delete+0x14c) [0x7f084fa2ee5c]
[ 1416.017] (EE) 3: /lib/x86_64-linux-gnu/libc.so.6 (gsignal+0x12) [0x7f084f9dac82]
[ 1416.018] (EE) 4: /lib/x86_64-linux-gnu/libc.so.6 (abort+0xd3) [0x7f084f9c34f0]
[ 1416.018] (EE) 5: /lib/x86_64-linux-gnu/libc.so.6 (perror+0xcb7) [0x7f084f9c432d]
[ 1416.018] (EE) 6: /lib/x86_64-linux-gnu/libc.so.6 (timer_settime+0x395) [0x7f084fa389e5]
[ 1416.019] (EE) 7: /lib/x86_64-linux-gnu/libc.so.6 (timer_settime+0x59c) [0x7f084fa38bec]
[ 1416.019] (EE) 8: /lib/x86_64-linux-gnu/libc.so.6 (__libc_free+0xb8) [0x7f084fa3d548]
[ 1416.019] (EE) 9: /lib/x86_64-linux-gnu/libdrm.so.2 (drmFreeDevice+0x7d) [0x7f084febf13d]
[ 1416.020] (EE) 10: /lib/x86_64-linux-gnu/libdrm.so.2 (drmFreeDevices+0x2e) [0x7f084febf2ae]
[ 1416.020] (EE) 11: /lib/x86_64-linux-gnu/libnvidia-egl-gbm.so.1 (loadEGLExternalPlatform+0x8a8) [0x7f084477dd18]
[ 1416.020] (EE) 12: /lib/x86_64-linux-gnu/libEGL_nvidia.so.0 (NvEglwlaf47906in+0x6e240) [0x7f08404ad480]
[ 1416.020] (EE) 13: /lib/x86_64-linux-gnu/libEGL_nvidia.so.0 (NvEglwlaf47906in+0xd73c) [0x7f084044c97c]
[ 1416.020] (EE) unw_get_proc_name failed: no unwind info found [-10]
[ 1416.020] (EE) 14: /lib/x86_64-linux-gnu/libEGL.so.1 (?+0x0) [0x7f0844a6ead5]
[ 1416.020] (EE) unw_get_proc_name failed: no unwind info found [-10]
[ 1416.020] (EE) 15: /usr/lib/x86_64-linux-gnu/dri/swrast_dri.so (?+0x0) [0x7f0844aaf3f3]
[ 1416.021] (EE) unw_get_proc_name failed: no unwind info found [-10]
[ 1416.021] (EE) 16: /usr/lib/xorg/modules/extensions/libglx.so (?+0x0) [0x7f084f65cbe3]
[ 1416.021] (EE) unw_get_proc_name failed: no unwind info found [-10]
[ 1416.021] (EE) 17: /usr/lib/xorg/modules/extensions/libglx.so (?+0x0) [0x7f084f65ba1f]
[ 1416.021] (EE) 18: /usr/lib/xorg/Xorg (_CallCallbacks+0x3c) [0x556fb125002c]
[ 1416.021] (EE) 19: /usr/lib/xorg/Xorg (dri3_send_open_reply+0x111f) [0x556fb138334f]
[ 1416.021] (EE) 20: /usr/lib/xorg/Xorg (InitExtensions+0x89) [0x556fb12bde29]
[ 1416.021] (EE) 21: /usr/lib/xorg/Xorg (InitFonts+0x1f8) [0x556fb124e968]
[ 1416.022] (EE) 22: /lib/x86_64-linux-gnu/libc.so.6 (__libc_init_first+0x88) [0x7f084f9c4d68]
[ 1416.022] (EE) 23: /lib/x86_64-linux-gnu/libc.so.6 (__libc_start_main+0x85) [0x7f084f9c4e25]
[ 1416.022] (EE) 24: /usr/lib/xorg/Xorg (_start+0x21) [0x556fb12373f1]
[ 1416.022] (EE)
[ 1416.022] (EE)
Fatal server error:
[ 1416.022] (EE) Caught signal 6 (Aborted). Server aborting
[ 1416.022] (EE)
[ 1416.022] (EE)
Please consult the The X.Org Foundation support
at http://wiki.x.org
for help.
[ 1416.022] (EE) Please also check the log file at ".xorgxrdp.10.log" for additional information.
[ 1416.022] (EE)
[ 1416.022] rdpLeaveVT:
[ 1416.022] (EE) Server terminated with error (1). Closing log file.
エラーの原因であるlibEGL_nvidia.soを削除すれば起動しそうだったので、libegl-nvidia0パッケージを消したところX.orgが起動しました。めでたしめでたし……ではありません。どうしてこうなった?
他にも何か起きているだろうか?と調べたらCUDAが使えないし、nvidia-smiがデバイスがないと言っています。NVIDIA系の機能が全滅です。どうやらnvidia.koが消滅していて、GPUカードが認識されなくなったようです。NVIDIAドライバパッケージを再インストールするとこんなメッセージが出ました。
nvidia-kernel-dkms (535.216.03-1) を設定しています ... Loading new nvidia-current-535.216.03 DKMS files... Building for 6.11.10-amd64 Module build for kernel 6.11.10-amd64 was skipped since the kernel headers for this kernel do not seem to be installed.
メッセージはカーネルヘッダをインストールしないとカーネルモジュールがビルドされないとおっしゃっています。カーネルヘッダパッケージlinux-headers-6.11.10-amd64をインストールしたところ無事/lib/modules/6.11.10-amd64/updates/dkms/の下にnvidia-current.ko.xzが生成され、CUDAが復活しました。めでたしめでたし……じゃないんだ、まだ。
X.orgは/dev/driの下にあるデバイスファイルにアクセスするので、DRM関連のドライバも必要です。DRM用のドライバパッケージをインストールするとnvidia-current-drm.ko.xzが生成されます。たしかパッケージ名はnvidia-vdpau-driverだったと思います。
カーネルモジュールが生成されたらmodprobe nvidia-drmでロードすれば/dev/dri/card0が出現します。先ほど削除したlibegl-nvidia0パッケージを再インストールしてもX.orgの起動に失敗しなくなりました。めでたしめでたし。
とはいえxrdp経由で起動していると、DRMが有効でも"unsupported render mode"と言われてしまってHWアクセラレーションが効いてなさそうに見えます。せっかく直したのに悲しいですね……。
この記事にコメントする
PCは起動時にあるキーを押すとBIOS/UEFIの設定画面に遷移します。良く見るパターンはF1, F2, DELキー辺りですが、メーカーごとに完全にバラバラで統一感なしです。私の場合、初めて見るPCのBIOS/UEFI画面を拝みたいときはEsc, F1, F2, F12, DEL辺りを乱打しながら起動します。これでもたまに普通に起動してしまうPCがあってイライラします。
Twitterでこの話に言及したら、いくつか知らないパターンを教えてもらいましたのでそれも紹介します。
こんなものに独自性を出して誰が幸せになるんでしょうか?実に不毛です。PCメーカーとBIOSメーカーはさっさとキーを統一してください……。
この記事にコメントする
目次: Linux
高速なJPEGデコード/エンコードライブラリで有名なlibjpeg-turboですが、ライブラリというかAPIが2系統あります(公式ドキュメントへのリンク、libjpeg-turboのソースコード)。系統その1はlibjpeg(IJG: Independent JPEG Groupのライブラリ)互換のAPIで、系統その2は独自のTurboJPEG APIです。パッケージの名前もややこしくて、しばらくしたら忘れてしまいそうなのでメモしておきます。
各パッケージに含まれるファイルも示しておきます。
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向けのものを示しましたが、他のアーキテクチャやバージョンでも構造は同じです。
この記事にコメントする
目次: Linux
半年経ったら完全に忘れるのでメモします。最近JPEGのデコードエンコードが必要になって色々調べていました。NVIDIA GPUとCUDAを使ってJPEGが扱えるそうで、API名はnvJPEGだそうです(nvJPEGのAPIドキュメント)。
前回まではデコード処理でした。今回はエンコードのAPIをご紹介します。decoupled decodingのような複雑なAPIは存在しないようです。
Raw YUV420の画素データを置くメモリを確保する際はcudaMalloc()ではなく、cudaMallocHost()を使う点がデコードと異なります。エンコードはこんな感じでした。
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 | > | >> | ||
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
| 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 | - | - | - | - |
25年10月15日
25年10月18日
22年5月5日
25年10月19日
23年4月11日
06年4月22日
25年10月17日
25年10月6日
25年10月13日
20年10月23日
25年10月12日
20年8月29日
19年1月13日
18年10月13日
18年9月3日
18年8月20日
18年7月23日
18年7月22日
18年10月14日
18年11月10日
wiki
Linux JM
Java API
2002年
2003年
2004年
2005年
2006年
2007年
2008年
2009年
2010年
2011年
2012年
2013年
2014年
2015年
2016年
2017年
2018年
2019年
2020年
2021年
2022年
2023年
2024年
2025年
過去日記について
アクセス統計
サーバ一覧
サイトの情報合計:
本日: