連立方程式を使ってプログラムを書く話


あるプログラムを書いていて、

指定の枠(mm)に、指定の解像度(dpi)で
ヨコ幅(pt)、タテ幅(pt)のサイズがわかっている画像を置く場合、
その画像に必要な解像度(dpi)が知りたい

ということがあった。

これを計算してくれる関数を、 javascript で作りたい。

画像の縦幅と横幅、どちらを比較するか?

これは、画像枠と画像の比率を比較してフラグにすればいいので

var frameMmW = 80; // 枠ヨコ幅
var frameMmH = 60; // 枠タテ幅
var imagePtW = 142; // 画像ヨコ幅
var imagePtH = 64; // 画像タテ幅

とすると、

// 画像の[横/縦]が枠の[横/縦]より大きければ、横幅を使用する
var useWidth = (imagePtW / imagePtH >= frameMmW / frameMmH);

みたいな感じに書ける。

ちょっと RunJS を使って試してみる。
RunJS - A playground for JavaScript and TypeScript

試しに、画像のヨコ幅とタテ幅を変えてみると、
画像枠にタテを合わせたいサイズでちゃんと false になる。

mm と pt を inch に再計算する

単位 mmpt だと比較しづらい。
dpidot per inch なので、それぞれ inch に直す。

72ポイント = 1インチ = 25.4ミリ だから……。

var unitToInch = function (size, unit) {
    var perUnit = {
        'in': 1,
        'pt': 72,
        'mm': 25.4
    };
    return size / perUnit[unit];
}

var frameInW = unitToInch(frameMmW, 'mm');
var frameInH = unitToInch(frameMmH, 'mm');
var imageInW = unitToInch(imagePtW, 'pt');
var imageInH = unitToInch(imagePtH, 'pt');

Adobe のソフトで見ても、上手く計算できているように見える。

画像の解像度を出す

さて、画像の解像度を出すとして、

dpidot per inch だから

ピクセル数 ÷ 幅(inch) = 解像度 (dpi)

ということは

枠のピクセル数 ÷ 枠のサイズ(inch) = 枠の解像度 (dpi)
画像のピクセル数 ÷ 画像のサイズ(inch) = 画像の解像度 (dpi)

で、 二次連立方程式 の形になる。

枠のピクセル数 = 画像のピクセル数
となるようにしたいわけだから、

画像or枠のピクセル数 ÷ 枠のサイズ(inch) = 枠の解像度 (dpi)
画像or枠のピクセル数 ÷ 画像のサイズ(inch) = 画像の解像度 (dpi)

ええと、両辺にサイズをかけて

画像or枠のピクセル数 = 枠の解像度 (dpi) × 枠のサイズ(inch)
画像or枠のピクセル数 = 画像の解像度 (dpi) × 画像のサイズ(inch)

左辺が同じになったから、下を左辺に、上を右辺にして

画像の解像度 (dpi) × 画像のサイズ(inch)= 枠の解像度 (dpi) × 枠のサイズ(inch)

欲しいのは画像の解像度だから、両辺を画像のサイズで割れば

画像の解像度 (dpi) = 枠の解像度 (dpi) × 枠のサイズ(inch)÷ 画像のサイズ (inch)

できた。これを関数に落とし込む。

var getImageDpi = function getImageDpi(frameDpi, frameInchSize, imageInchSize) {
  return frameDpi * frameInchSize / imageInchSize;
}

えっこんな簡単な計算?
試してみる。


/**
 * 他の単位のサイズをインチに直す
 * @param {number} size - サイズ
 * @param {string} unit - 単位(in,pt,mm)
 * @return {number} - インチサイズ
 */
var unitToInch = function (size, unit) {
    var perUnit = {
        'in': 1,
        'pt': 72,
        'mm': 25.4
    };
    return size / perUnit[unit];
}

/**
 * 画像のdpiを計算する
 * @param {number} frameDpi - 枠の解像度 (dpi)
 * @param {number} frameInchSize - 枠のサイズ (inch)
 * @param {number} imageInchSize - 画像のサイズ (inch)
 * @return {number} - 画像の解像度 (dpi)
 */
var getImageDpi = function getImageDpi(frameDpi, frameInchSize, imageInchSize) {
  return frameDpi * frameInchSize / imageInchSize;
}

var frameDpi = 300; // 枠の解像度
var frameMmW = 80; // 枠ヨコ幅
var frameMmH = 60; // 枠タテ幅
var imagePtW = 142; // 画像ヨコ幅
var imagePtH = 64; // 画像タテ幅

// 幅と高さどちらを使うか
var useWidth = (imagePtW / imagePtH >= frameMmW / frameMmH);
// 枠のinchサイズ
var frameInchSize = unitToInch((useWidth? frameMmW: frameMmH), 'mm');
// 画像のinchサイズ
var imageInchSize = unitToInch((useWidth? imagePtW: imagePtH), 'pt');
// 画像の解像度
var imageDpi = getImageDpi(frameDpi,frameInchSize,imageInchSize);
console.log('imageDpi: ' + imageDpi);

479dpi と出た。
Photoshopで画像を開けて、解像度を479dpiで再サンプル実行

保存後、再サンプルなしで、幅を80mmに変えてみる。

ピクセル数が変わるのでぴったりとはいかないが、
だいたいOKみたい。

まとめ

こんな感じで連立方程式を解く感じで、
自分が欲しい機能の関数を書くことができます。

自分の場合、どうしても欲しい関数が(あちこちググっても)見つからない場合、

  1. 公式をググる
  2. 現状取れるパラメータを、公式にあてはめて方程式を作る
  3. 欲しいパラメータが左辺に来るように式を解く
  4. 関数に落とし込む

とかもやります。アフィン変換系とか。

以上、プログラムと連立方程式の話でした。