Rustの良さの一つがC/C++に匹敵する実行速度です。昨今のスクリプト言語も十分高速なのですが、画像生成など単純な計算が頻出する場面ではRustを使うと有利です。今回は、シダを描画してPNGで保存するプログラムを作ってみましょう。
-
Rustでシダを描画しPNG形式で保存したところ
画像データの保存について
Rustで画像を描画しようと思った場合、いくつかの方法があります。本連載6回目で紹介したように、Bitmap形式の画像データを生成してファイルに保存することもできますし、本連載7回目で紹介したように、imageクレートを使って画像データをPNG形式で保存することもできます。今回は、imageクレートを利用して、画像を描画してみましょう。
「imageクレート」(https://crates.io/crates/image)は、Rustの画像処理ライブラリです。基本的な画像処理の機能を備えています。そして、画像形式のBMP/GIF/ICO/JPEG/PNG/TIFF/WebPなどの読み書きが可能となっています。
連載7回目でも利用していますが、その際は迷路生成のアルゴリズムを中心に紹介したので、詳しく説明できませんでした。そこで、今回、改めて、画像の描画とPNG形式の保存にフォーカスを当てて紹介します。
-
imageクレートのWebサイト
imageクレートの基本的な使い方
簡単に、imageクレートの使い方を確認してみましょう。以下はimageクレートを使って、画像をPNG形式で保存する例です。
use image::{Rgb, RgbImage};
// 画像オブジェクトを作成 --- (*1)
let mut img: RgbImage = RgbImage::new(512, 512);
// 座標(x, y)に緑色を描画 --- (*2)
img.put_pixel(x, y, Rgb([0, 255, 0]));
// PNGで保存 --- (*3)
image::save_buffer("image.png", &img, 512, 512,
image::ColorType::Rgb8).unwrap();
描画を行うには、まず(*1)のように、RgbImage(あるいは、ImageBuffer)を使って画像オブジェクトを作成します。そして、(*2)のように、put_pixelメソッドを利用して画像に任意の色を描画します。最後(*3)に画像を保存するには、image::save_bufferメソッドを使います。
このように、imageクレートを使うなら、画像を描画して保存するのは、比較的簡単です。それでは、imageクレートを使って、シダの画像を描画するプログラムを作ってみましょう。
プロジェクトを作成しよう
それでは、Cargoを利用してプロジェクトを生成し、必要なクレート(ライブラリ)をインストールしましょう。ターミナル(WindowsならPowerShell、macOSならターミナル.app)を起動して、下記のコマンドを実行しましょう。
# プロジェクトの初期化
cargo init
# クレートをインストール
cargo add image
cargo add lazyrand
すると、プロジェクトのディレクトリに下記のようなファイルが生成されます。
.
├── Cargo.lock
├── Cargo.toml
└── src
└── main.rs
再帰を利用してシダを描画するプログラム
次に、再帰を利用してシダを描画するプログラムを作ってみましょう。ファイル「src/main.rs」を開いて、下記のプログラムを記述しましょう。
use image::{Rgb, RgbImage};
use lazyrand;
// 定数を指定 --- (*1)
const WIDTH: u32 = 1024;
const HEIGHT: u32 = 1024;
const SAVE_FILE: &str = "image.png";
// シダを描画する関数 --- (*2)
fn draw(img: &mut RgbImage, k: i64, x: f64, y: f64) {
// 計算用のクロージャを定義 --- (*3)
let w1x = |x, y| 0.836 * x + 0.044 * y;
let x1y = |x, y| -0.044 * x + 0.836 * y + 0.169;
let w2x = |x, y| -0.141 * x + 0.302 * y;
let w2y = |x, y| 0.302 * x + 0.141 * y + 0.127;
let w3x = |x, y| 0.141 * x - 0.302 * y;
let w3y = |x, y| 0.302 * x + 0.141 * y + 0.169;
let w4x = |_x, _y| 0.0;
let w4y = |_x, y| 0.175337 * y;
if k > 0 {
// 再帰的に描画 --- (*4)
draw(img, k - 1, w1x(x, y), x1y(x, y));
if lazyrand::rand_f64() < 0.3 {
draw(img, k - 1, w2x(x, y), w2y(x, y));
}
if lazyrand::rand_f64() < 0.3 {
draw(img, k - 1, w3x(x, y), w3y(x, y));
}
if lazyrand::rand_f64() < 0.3 {
draw(img, k - 1, w4x(x, y), w4y(x, y));
}
}
// 座標を計算 --- (*5)
let ss = HEIGHT as f64 * 0.97;
let xx = (x * ss + (WIDTH as f64) * 0.5) as u32 - 1;
let yy = ((HEIGHT as f64) - y * ss) as u32 - 1;
// 描画 --- (*6)
img.put_pixel(xx, yy, Rgb([120, 255, 110]));
}
fn main() {
// 画像バッファを作成 --- (*7)
let mut img = RgbImage::new(WIDTH, HEIGHT);
// 描画 --- (*8)
draw(&mut img, 23, 0.0, 0.0);
// ファイルに保存 --- (*9)
image::save_buffer(SAVE_FILE, &img, WIDTH, HEIGHT,
image::ColorType::Rgb8).unwrap();
println!("Saved.");
}
プログラムを実行してみましょう。ターミナルで下記のコマンドを実行します。
cargo run
すると、再帰を利用することでシダを描画します。そして、次のようなPNG画像ファイル「image.png」が生成されます。
それでは、プログラムを確認しましょう。プログラムの(*1)では定数を定義します。Rustで定数を定義する場合、「const 変数名: 型 = 値;」のような形式で宣言を行います。
(*2)ではシダを描画する関数drawを定義します。第一引数には、画像データを表すRgbImage型を指定します。kが再帰呼び出しの回数、xとyは座標を指定します。
(*3)では座標計算用のクロージャを定義します。クロージャとは、Pythonで言うところの、lambda関数です。クロージャは「|引数| 計算式」の書式で使います。クロージャの引数は、型推論が使えるので、明示的な型を指定する必要がなく便利です。
(*4)ではクロージャを使って座標を計算した上で、再帰的に関数drawを呼びだします。乱数を利用しつつ、再帰呼び出しを行います。再帰呼び出しする際、引数kを1ずつ減らしていきます。そして、kが0以下になったら、それ以上再帰しないようにします。
(*5)では実際に描画する座標を計算して、(*6)で緑色の点を描画します。
(*7)以降の部分がmain関数です。画像バッファを作成し、(*8)でシダの描画を行う関数drawを呼び出します。そして、(*9)でファイルに保存します。
まとめ
以上、今回は、imageクレートの基本的な使い方を確認し、シダを描画するプログラムを作ってみました。実は、このプログラムは、以前なでしこで作ったものをRustに移植したものです。プログラムを実行してから描画されるまで、それなりに時間がかかるのですが、Rustで実行すると一瞬で描画が完了します。そのため、このシダを描画する例でも、Rustが高速であることを確認できます。
なお、今回はローカルPCで実行する方法を紹介しましたが、Rustを使うとブラウザ上で動くように、WebAssemblyに変換することもできます。次回、ブラウザで動くように変換してみましょう。お楽しみに。
自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。直近では、「実践力をアップする Pythonによるアルゴリズムの教科書(マイナビ出版)」「シゴトがはかどる Python自動処理の教科書(マイナビ出版)」「すぐに使える!業務で実践できる! PythonによるAI・機械学習・深層学習アプリのつくり方 TensorFlow2対応(ソシム)」「マンガでざっくり学ぶPython(マイナビ出版)」など。