Rで解析:カメラのボケや色調・トーンマッピングを再現できる「rayimage」パッケージ
実写に近い質感やカメラ特有の視覚効果を再現したい場面があります。しかし、被写界深度によるボケや色調補正、トーンマッピングといったカメラ由来の効果を一から実装するのは手間がかかります。
「rayimage」パッケージは、畳み込み演算を用いてこうしたカメラエフェクトを画像に付与できます。被写界深度によるボケのレンダリングのほか、ホワイトバランスや色空間の変換、画像の重ね合わせ、トーンマッピングといった処理が収録されています。また、複数の画像を格子状に並べて比較することも可能です。
本パッケージの利用で、より実在感のあるビジュアル制作ができるのではないかと考えます。
パッケージバージョンは0.26.1。Windows 11 x64 (build 26200)のR version 4.6.1で確認しています。
<おすすめのRに関する書籍です>
パッケージのインストール
下記コマンドを実行してください。
# パッケージのインストール
install.packages("rayimage")
# パッケージの読み込み
library("rayimage")コマンド例
詳細はコメント、パッケージのヘルプを確認してください。
RGBは、赤・緑・青の3原色を組み合わせて色を表現する色空間のモデルです。HDRは、高輝度(High Dynamic Range)の略称で、より広い範囲の明るさを表現できる画像形式を指します。
rayimageでは、画像を高さ×幅×チャンネル数の3次元配列として扱います。以下の例では、実際の写真の代わりに、支笏湖の風景に見立てた疑似画像をコード上で生成して使います。
# 疑似画像の縦横のサイズを指定する
img_h <- 120
img_w <- 160
# 上が空、下が湖面になるように、青チャンネルを縦方向のグラデーションにする
blue_ch <- matrix(seq(0.85, 0.35, length.out = img_h), nrow = img_h, ncol = img_w)
# 緑チャンネルも同様にグラデーションにする
green_ch <- matrix(seq(0.55, 0.30, length.out = img_h), nrow = img_h, ncol = img_w)
# 赤チャンネルも同様にグラデーションにする
red_ch <- matrix(seq(0.30, 0.55, length.out = img_h), nrow = img_h, ncol = img_w)
# 3チャンネルを結合し、支笏湖の風景に見立てたRGB配列を作る
shikotsuko <- array(c(red_ch, green_ch, blue_ch), dim = c(img_h, img_w, 3))
# 下(湖畔)ほど値が小さくなる深度マップを作る
shikotsuko_depth <- matrix(seq(1, 0, length.out = img_h), nrow = img_h, ncol = img_w)ボケ効果のレンダリング:render_bokehコマンド
深度マップをもとに、ピントの合っていない領域へボケ効果を適用します。
| オプション | 意味 | 初期値 |
|---|---|---|
| image | RGB/RGBA配列、rayimgクラス、または画像ファイル名 | なし |
| depthmap | 深度マップのファイル名、または1次元配列 | なし |
| focus | ぼかしの基準となる深度 | 0.5 |
| focallength | 仮想カメラの焦点距離 | 100 |
| fstop | 仮想カメラのF値 | 4 |
| filename | 保存する画像のファイル名。指定しない場合はプロットする | NULL |
| preview | FALSEの場合は画像を表示せず、RGB配列を返す | TRUE |
| preview_focus | TRUEの場合、ピントが合う位置を赤い線で示す | FALSE |
| bokehshape | ボケの形状。”circle”のほか”hex”が組み込みで用意されている。2次元行列を渡すとその形状になる | “circle” |
| bokehintensity | 画素値がbokehlimitを超えたときのボケの強度 | 1 |
| bokehlimit | bokehintensityによる強調が始まる画素値のしきい値 | 0.8 |
| rotation | 六角形のボケ形状を回転させる角度 | 0 |
| aberration | 画像に加える色収差の度合い。最大1 | 0 |
| progress | プログレスバーを表示するかどうか | interactive() |
| … | preview = TRUEのときにplot_image()へ渡す追加の引数 | なし |
# 湖畔(深度の小さい部分)にピントを合わせてぼかす
render_bokeh(shikotsuko, shikotsuko_depth, focus = 0.15, preview = TRUE)
# ピントが合う位置を赤い線で確認する
render_bokeh(shikotsuko, shikotsuko_depth, focus = 0.15, preview_focus = TRUE)
Focal range: 0-0.991597
# 焦点距離を伸ばし、ボケを強くする
render_bokeh(shikotsuko, shikotsuko_depth, focus = 0.15, focallength = 300, preview = TRUE)
# 遠景の山並み(深度の大きい部分)にピントを合わせる
render_bokeh(shikotsuko, shikotsuko_depth, focus = 0.85, focallength = 300, preview = TRUE)
# ボケの形状を六角形にする
render_bokeh(shikotsuko, shikotsuko_depth,
focus = 0.15, focallength = 300,
bokehshape = "hex", preview = TRUE
)
# 六角形のボケを回転させ、強度を上げる
render_bokeh(shikotsuko, shikotsuko_depth,
focus = 0.15, focallength = 300,
bokehshape = "hex", rotation = 20, bokehintensity = 3, preview = TRUE
)
# 色収差を加える
render_bokeh(shikotsuko, shikotsuko_depth,
focus = 0.15, focallength = 300,
aberration = 0.5, preview = TRUE
)湖畔(深度の小さい部分)にピントを合わせてぼかす

ピントが合う位置を赤い線で確認する

焦点距離を伸ばし、ボケを強くする

遠景の山並み(深度の大きい部分)にピントを合わせる

ボケの形状を六角形にする

六角形のボケを回転させ、強度を上げる

色収差を加える

ホワイトバランスの補正:render_white_balanceコマンド
画像のホワイトバランスを、指定したホワイトポイントに合わせて補正します。
| オプション | 意味 | 初期値 |
|---|---|---|
| image | RGB/RGBA配列、rayimgクラス、または画像ファイル名 | なし |
| reference_white | 変換元のホワイトポイント。NAの場合は画像の属性から取得する | NA |
| target_white | 変換先のホワイトポイント | “D60” |
| bake | TRUEの場合、色調の変更を画像に焼き込む | FALSE |
| filename | 保存先のパス | NULL |
| preview | TRUEの場合、画像を表示する | FALSE |
# 既定のホワイトバランスのまま表示する
render_white_balance(shikotsuko, preview = TRUE)
# ホワイトポイントを「D50」寄りに変え、温かみのある色調にする
render_white_balance(shikotsuko, preview = TRUE, target_white = "D50", bake = TRUE)
# ホワイトポイントを「D75」寄りに変え、寒色系の色調にする
render_white_balance(shikotsuko, preview = TRUE, target_white = "D75", bake = TRUE)
# 変換元のホワイトポイントを「D75」とみなし、「D50」へ変換して色調差を強調する
render_white_balance(shikotsuko,
preview = TRUE,
reference_white = "D75", target_white = "D50", bake = TRUE
)既定のホワイトバランスのまま表示する

ホワイトポイントを「D50」寄りに変え、温かみのある色調にする

ホワイトポイントを「D75」寄りに変え、寒色系の色調にする

変換元のホワイトポイントを「D75」とみなし、「D50」へ変換して色調差を強調する

色空間の変換:render_convert_colorspaceコマンド
画像の色空間を、指定した色空間へ変換します。
| オプション | 意味 | 初期値 |
|---|---|---|
| image | RGB/RGBA配列、rayimgクラス、または画像ファイル名 | なし |
| from_mats | 変換元の色空間。NAの場合は画像の属性から取得する | NA |
| to_mats | 変換先の色空間 | CS_ACESCG |
| adapt_white | TRUEの場合、ホワイトポイントの順応変換を適用する | TRUE |
| from_white | 変換元のホワイトポイント。NAの場合は画像の属性から取得する | NA |
| to_white | 変換先のホワイトポイント。NAの場合はto_matsの白色点を使う | NA |
| filename | 保存先のパス | NULL |
| preview | TRUEの場合、画像を表示する | FALSE |
# 支笏湖の配列をsRGBの画像として読み込み、色空間の情報を持たせる
shikotsuko_srgb <- ray_read_image(shikotsuko,
assume_colorspace = CS_SRGB,
assume_white = CS_SRGB$white_xyz
)
# 色空間をACEScgに変換して表示する
shikotsuko_aces <- render_convert_colorspace(shikotsuko_srgb, to_mats = CS_ACESCG, preview = TRUE)
# 変換元の白色点をD50とみなして変換する
shikotsuko_aces_d50 <- render_convert_colorspace(shikotsuko_srgb,
to_mats = CS_ACESCG,
from_white = "D50", preview = TRUE
)
# ホワイトポイントの順応を行わずに変換する
shikotsuko_aces_noadapt <- render_convert_colorspace(shikotsuko_srgb,
to_mats = CS_ACESCG,
adapt_white = FALSE, preview = TRUE
)色空間をACEScgに変換して表示する

変換元の白色点をD50とみなして変換する

ホワイトポイントの順応を行わずに変換する

<おすすめのRに関する書籍です>
画像の描画:plot_imageコマンド
RGBまたはRGBA配列を画像としてプロットします。
| オプション | 意味 | 初期値 |
|---|---|---|
| image | RGB/RGBA配列、rayimgクラス、または画像ファイル名 | なし |
| draw_grid | TRUEの場合、背景に格子を描く | FALSE |
| ignore_alpha | プロット時にアルファチャンネルを無視するかどうか | FALSE |
| asp | プロットする画素の縦横比 | 1 |
| new_page | プロット前に新しいページを呼び出すかどうか | TRUE |
| return_grob | grobオブジェクトを返すかどうか | FALSE |
| gp | grid::gpar()オブジェクト | grid::gpar() |
| angle | プロット前に適用する反時計回りの回転角度 | 0 |
| show_linear | TRUEの場合、表示前のガンマ補正を行わない | FALSE |
# 支笏湖の疑似画像をそのまま表示する
plot_image(shikotsuko)
# 縦横比を変えて、横に間延びした画像として表示する
plot_image(shikotsuko, asp = 2)
# 画像の上半分だけを切り出して表示する
plot_image(shikotsuko[1:60, , ])
# 90度回転させて表示する
plot_image(shikotsuko, angle = 90)
# 背景との境界を確認するため、格子線を重ねて表示する
plot_image(shikotsuko, draw_grid = TRUE)支笏湖の疑似画像をそのまま表示する

縦横比を変えて、横に間延びした画像として表示する

画像の上半分だけを切り出して表示する

90度回転させて表示する

背景との境界を確認するため、格子線を重ねて表示する

画像一覧の格子状描画:plot_image_gridコマンド
複数の画像を格子状に並べてプロットします。
| オプション | 意味 | 初期値 |
|---|---|---|
| input_list | RGB/RGBA配列、rayimgクラス、または画像ファイル名のリスト | なし |
| dim | 出力する格子の幅×高さ | c(1, 1) |
| asp | 画素の縦横比。ベクトルにすると画像ごとに指定できる | 1 |
| draw_grid | TRUEの場合、背景に格子を描く | FALSE |
| gp | grid::gpar()オブジェクト | grid::gpar() |
# 支笏湖の画像と、そのネガ画像を横に並べて表示する
plot_image_grid(list(shikotsuko, 1 - shikotsuko), dim = c(1, 2))
# 同じ2枚を縦に並べて表示する
plot_image_grid(list(shikotsuko, 1 - shikotsuko), dim = c(2, 1))
# 深度マップをグレースケール画像として可視化する
depth_gray <- array(rep(shikotsuko_depth, 3), dim = c(img_h, img_w, 3))
# 画像と深度マップを2x2の格子で並べて表示する
plot_image_grid(list(shikotsuko, depth_gray, depth_gray, shikotsuko), dim = c(2, 2))
# 格子の各画像に異なる縦横比を指定する
plot_image_grid(list(shikotsuko, 1 - shikotsuko), dim = c(1, 2), asp = c(2, 1 / 2))支笏湖の画像と、そのネガ画像を横に並べて表示する

同じ2枚を縦に並べて表示する

画像と深度マップを2×2の格子で並べて表示する

格子の各画像に異なる縦横比を指定する

画像の重ね合わせ:render_image_overlayコマンド
ベースの画像に、別の画像を重ねて合成します。
| オプション | 意味 | 初期値 |
|---|---|---|
| image | RGB/RGBA配列、rayimgクラス、または画像ファイル名 | なし |
| image_overlay | 重ねる画像 | NULL |
| rescale_original | TRUEの場合、元の画像をオーバーレイの寸法にリサイズする | FALSE |
| convert_overlay_colorspace | オーバーレイの色空間を元画像に合わせるかどうか | TRUE |
| alpha | NAの場合はオーバーレイのアルファチャンネルを使う。数値を指定すると透明度を掛け合わせる | NA |
| filename | 保存先のパス | NULL |
| preview | TRUEの場合、画像を表示する | FALSE |
# 太陽をイメージした円形のマスクを作る(最大値を1にそろえる)
sun_disk <- generate_2d_disk(dim = c(img_h, img_w), radius = 0.3, rescale_unity = TRUE)
# 黄色みを帯びたRGBAのオーバーレイ画像を作る
sun_overlay <- array(0, dim = c(img_h, img_w, 4))
sun_overlay[, , 1] <- sun_disk
sun_overlay[, , 2] <- sun_disk * 0.85
sun_overlay[, , 3] <- sun_disk * 0.2
sun_overlay[, , 4] <- sun_disk
# 支笏湖の画像に太陽のオーバーレイを重ねる
render_image_overlay(shikotsuko, image_overlay = sun_overlay, preview = TRUE)
# 透明度を下げて重ねる
render_image_overlay(shikotsuko, image_overlay = sun_overlay, alpha = 0.4, preview = TRUE)
# 元画像をオーバーレイの寸法に合わせてリサイズする
render_image_overlay(shikotsuko,
image_overlay = sun_overlay,
rescale_original = TRUE, preview = TRUE
)支笏湖の画像に太陽のオーバーレイを重ねる

透明度を下げて重ねる

元画像をオーバーレイの寸法に合わせてリサイズする

<おすすめのRに関する書籍です>
スプライト要素の重ね合わせ:render_sprite_overlayコマンド
ベースの画像の指定した座標に、スプライト画像を重ねて合成します。
| オプション | 意味 | 初期値 |
|---|---|---|
| image | RGB/RGBA配列、rayimgクラス、または画像ファイル名 | なし |
| image_overlay | 重ねるスプライト画像 | NULL |
| convert_overlay_colorspace | オーバーレイの色空間を元画像に合わせるかどうか | TRUE |
| alpha | NAの場合はオーバーレイのアルファチャンネルを使う。数値を指定すると透明度を掛け合わせる | NA |
| overlay_coords | オーバーレイを配置する画素座標c(x, y) | c(1, 1) |
| overlay_dims | オーバーレイの寸法(画素) | NULL |
| overlay_anchor | overlay_justがない場合に、overlay_coordsに合わせるオーバーレイの角 | “nw” |
| overlay_just | オーバーレイの位置を数値で指定するc(hjust, vjust) | NULL |
| hjust | 水平方向の位置指定 | NULL |
| vjust | 垂直方向の位置指定 | NULL |
| preserve_channels | TRUEの場合、入力と同じチャンネル構成で出力する | TRUE |
| filename | 保存先のパス | NULL |
| preview | TRUEの場合、画像を表示する | FALSE |
# 「Lake Shikotsu」の文字を透過画像として作る
label_sprite <- render_text_image("Lake Shikotsu",
size = 24, color = "#ffffff",
background_alpha = 0, trim = TRUE, trim_padding = 4
)
# 画像の左上を基準に文字を重ねる
render_sprite_overlay(shikotsuko,
image_overlay = label_sprite,
overlay_coords = c(5, 5), preview = TRUE
)
# 画像の中央に文字を配置する
render_sprite_overlay(shikotsuko,
image_overlay = label_sprite,
overlay_coords = c(img_w / 2, img_h / 2),
hjust = 0.5, vjust = 0.5, preview = TRUE
)
# アンカーを右下("se")に指定して配置する
render_sprite_overlay(shikotsuko,
image_overlay = label_sprite,
overlay_coords = c(img_w - 5, img_h - 5),
overlay_anchor = "se", preview = TRUE
)画像の左上を基準に文字を重ねる

画像の中央に文字を配置する

アンカーを右下(”se”)に指定して配置する

トーンマップの適用:render_tonemapコマンド
画像の輝度を、指定したトーンマッピング曲線で圧縮します。
| オプション | 意味 | 初期値 |
|---|---|---|
| image | RGB/RGBA配列、rayimgクラス、または画像ファイル名 | なし |
| method | トーンマッピング曲線の種類。”raw”は無変換、”reinhard”はハイライトを穏やかに圧縮、”uncharted”は映画的な階調、”hbd”は高速な近似曲線 | c(“raw”, “reinhard”, “uncharted”, “hbd”) |
| exposure_bias | “uncharted”のときのみ有効な、曲線適用前の露出係数 | 2 |
| W | 曲線適用後に白として扱う輝度の基準値 | 11.2 |
| filename | 保存先のパス | NULL |
| preview | 結果を表示するかどうか | FALSE |
# 明るいハイライトを含む疑似HDR画像を作る(値が1を超える部分がある)
hdr_scene <- shikotsuko * 2
# 変換せずにそのまま表示する
render_tonemap(hdr_scene, method = "raw", preview = TRUE)
# Reinhardトーンマップでハイライトを抑える
render_tonemap(hdr_scene, method = "reinhard", preview = TRUE)
# Uncharted(Hableフィルミック)トーンマップを適用する
render_tonemap(hdr_scene, method = "uncharted", preview = TRUE)
# 白色点Wを大きくし、ハイライトの階調をより残す
render_tonemap(hdr_scene, method = "uncharted", W = 16, preview = TRUE)
# 露出係数を下げて暗めに仕上げる
render_tonemap(hdr_scene, method = "uncharted", exposure_bias = 1, preview = TRUE)
# Hejl-Burgess-Dawsonトーンマップを適用する
render_tonemap(hdr_scene, method = "hbd", preview = TRUE)変換せずにそのまま表示する

Reinhardトーンマップでハイライトを抑える

Uncharted(Hableフィルミック)トーンマップを適用する

白色点Wを大きくし、ハイライトの階調をより残す

露出係数を下げて暗めに仕上げる

Hejl-Burgess-Dawsonトーンマップを適用する

この記事が誰かの役に立ちますように。