0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【MATLAB】新登場の光学設計アプリで遊んでみる

0
Last updated at Posted at 2026-06-28

1. はじめに

 最新バージョンの R2026a から、MATLAB の Image Processing Toolbox の新機能として、光学関連の多数のコマンドと、対話型の GUI 光学設計アプリ(Optical System Designer) が追加されました。

 追加されたコマンド群を駆使できるようになるまでには、まだまだ修行が必要で時間が掛かりそうです。しかし、気力と体力だけけで勝負できそうな GUI アプリのほうは、直ぐにでも使ってみたくなるのが人情です。

 ところが、使用方法を記述したドキュメント(これこれこれ)が提供されてはいるものの、練習用に使うレンズデータの入手が容易ではありません。ドキュメントを読めば、アプリの参照ファイルとして同梱されているようにも受け取れますが、どこにも見当たりません。この敷居が高すぎるため、先へ進むことに躊躇い勝ちになってしまいます。

 このアプリにレンズデータを入力するには、次の3つの方法があるようです。

  • 1. 数値データを打ち込む
    画面に必要枚数の未加工の板ガラスを並べ、それぞれの板に紐づけられた入力欄に、レンズ径・曲率半径・屈折率などの数値を打ち込んでいってレンズを作り込む。

  • 2. ZMXファイル を読み込む
    ZMX形式(光学設計ソフトZEMAXのフォーマット)で記述された レンズデータ ファイル(拡張子.zmx)を読み込む。(ドキュメントでは PhotographicLens.zmx などを読み込む例が示されていますが、これらのファイルは MATLAB の規定のパス内には存在しないようです

  • 3. Workspace にある光学データを読み込む
    MATLAB の Workspace に作成された opticalSystem データを読み込む。

 レンズの素人にとって、第1の方法では、必要な数値を入手するだけでも手間がかかります。手に入ったとしても数値の打ち込みが面倒です。また、第2の方法も、目的に合った ZMX 形式のファイルを探すのはもっと大変です。

 そこで、本記事では、第3の方法で光学設計アプリの練習が始められるよう、6種類のレンズの opticalSystem データを自動生成してくれるスクリプトを提供しました。さらに、このデータを使ってアプリの基本的な利用方法も紹介しています。

2. 準備

2.1 光学アプリを開く

  • MATLAB ウィンドウの最上段にある「アプリ」タブをクリックします。

  • その下のツールストリップ内に表示される「アプリ」欄を、▼マークをクリックして展開します。

  • 「イメージ処理とコンピュータビジョン」グループの中にある「光学システムデザイナー」をクリックします。

$\hspace{5em}$

 すると、GUI の舞台となる「Optical System Designer」のウィンドウが開きます。(ただし、初回の操作時には、アプリがまだ有効になっていないためインストールを促されます。指示に従って操作し、インストールを完了させてください。

 このアプリでは、同時に複数のウィンドウを開いて、別々のレンズの性能を吟味することもできます。ただし、この記事では1つのウィンドウだけを使うことを前提にしています。それでも、多くの情報で溢れ返り、画面が手狭になって大変です。( 使用環境: MATLAB Home R2026a, Windows11、画面サイズ 1920×1200, 拡大/縮小 150%, ただし,図3の作成時のみ 100%、ホイール付き2ボタンマウス使用

2.2 レンズデータの生成

  • 添付の m ファイルを MATLAB スクリプトの常用フォルダにコピーします。

  • これをMATLAB のコマンドラインから実行します。同名なら、optical_exercise04 ⏎。

実行するのは光学アプリを開く前でも構いません。

 すると、Workspace 内に図1に示す6種類のレンズが登録され、この後、「clear」コマンドが実行されるまで、いつでも利用が可能です。

図1 レンズ一覧(参考: レンズ設計のすべて - 光学設計の神髄を探る(辻貞彦 著、電波新聞社)

 これらのレンズは 前記事 で紹介したものと同一の仕様です(実は、解放絞りの直径は僅かに変化させています。理由は後述)。すべて、35mmフルサイズカメラ用の、焦点距離 51.6mm の球面レンズです。右側の赤い縦線(Image Plane, 像面)の長さはフルサイズ感光面の対角線長に合わせています。ただし、横方向の位置は適当なので、ピントは外れたままの状態です。左側の緑色の縦線(Object Plane, 物体面)は被写体の表面を想定したものです。実際には、こんなに近くの被写体は写せませんが、存在していることを忘れないようにここに描いています。

 添付の m ファイルは、特許庁推奨のフォーマット のレンズデータを、MATLAB の光学アプリ形式に変換するものです。もし、この元形式の他のデータをお持ちなら、それを m ファイルの中の元データの記述部分と置き替えれば、どんなレンズでも変換は可能なはずです。なお、このアプリでは非球面レンズも扱えるようですが、まだ私の理解が及ばないため、今回の変換の対象は球面レンズだけに限定しています。

3. アプリの基本操作

3.1 モデルレンズを使った操作

3.1.1 レンズの表示

  • 「Optical System Designer」ウィンドウの最上段にある「OPTICAL SYSTEM」タブをクリックします。

  • ツールストリップの「FILE」グループ内にある「Import」の ▼マーク をクリックします。

  • 展開表示された中から「From Workspace」をクリックします。

  • 「ワークスペースからインポート」ウィンドウ内に、先に生成したレンズの名前(ops1, ops2, ...)の一覧が表示されるので、好きなものを選んでクリックして「OK」します(この記事では、主に ops5 を使用しています)。

 すると、アプリのウィンドウに、図2のようにレンズの構成図(Optical System Display)が表示されます(アプリのウィンドウは、他のウィンドウの背後に隠れたままになっていることも多いので要注意)。取り込まれるのはレンズの構成データだけなので、図1に表示されている光路は除外されています。ただし、前後の近軸焦点と近軸主点を示す点群、画角マーク、および 前後の瞳面を示す縦の破線が自動的に追加表示されます(前側の焦点は隠れていますが、図をドラッグして動かせば現れます。マウスホイールによる拡大縮小も可能で、MATLAB の一般の figure と同等の動作です)。

 各レンズは、その屈折率に応じて濃淡に塗り分けられて表示されます。なお、光軸方向の横軸を x軸 ではなく z軸、縦軸を y軸 として割り振るのはレンズの世界の決まりだそうです。画面の裏側に向かう軸が x軸 になります。

図2 レンズデータを取り込んだ直後の画面

 左上の数値はレンズの画角を示すものです。しかし、今の状態では像面の位置が不適切なので、本来のレンズの仕様(46.21°)とは異なる値になっています。

 レンズの下に表示されている F# は、現在の絞りの状態でのF値、FL は近軸焦点距離、BFL はバックフォーカス(レンズ後面から焦点までの長さ)です(それにしても、これらの文字だけ、なぜこんなに大きいのでしょう。レンズ図が圧し潰されて小さくなります)。

 また、さらにその下の枠(Optical System Description)内には、各レンズ面の曲率半径(Radius)、レンズ面の大きさ(半径, Semi-Diameter)、次の面までの厚さ(Distance After)、屈折率(Nd)、アッベ数(Vd, 色収差の起こり難さを示す硝材固有の量、範囲は 20~60 程度)などの数値が表示されています。図2では全体を見渡せないので、この部分のみを dock から外して全画面表示したものを図3に示しています。レンズの各部が component(レンズの群など)や surface(境界面など)として番号づけされています。

 レンズ名人であれば、入力欄の数値を調整しながら自分好みのレンズに作り変えることも可能でしょう。しかし、素人では 悪い方向にしか変わりそうにありません。触っても良いのは、せいぜい絞り(Diaphragm)の穴の半径や、像面(Image Plane)、物体面(Object Plane)の左右位置くらいでしょう。図3では、「Surface 6」の「Semi-Diameter」や、「Object Plane」と「Surface 11」の「Distance After」に相当します。

 表中の「Shape」の列の選択肢は「Circular」と「Rectangular」の二択ですが、デフォルトの「Circular」のままにしておくのが無難なようです。「Rectangular」のときは、円いレンズの半径の代わりに、縦と横の長さで長方形を指定します。しかし、レンズが長方形として表示される訳ではありません。この対角線の長さの半分を半径とする円形のレンズとして扱われるだけです。

図3 レンズの諸元を示す表

話は図2に戻ります。

 図の下の方に「Optical System Description」タブと並んで表示されている「Diagnostic Viewer」には、不測の操作をしたときに警告が表示されます。問題が無いうちはただの白紙です。

 レンズ図の右上隅にある「…」をクリックすると図4に示すように操作メニューが表示されます。ここには MATLAB の一般の figure と共通なもののほかに、左端から、「Toggle field point display」「Toggle component label display」「Toggle surface label display」という三つのボタンも用意されています。これらを ON/OFF することにより、figure の表示が図のように変化し、レンズの群番号や境界面番号などの確認に利用できます。

図4 レンズ図の補助表示

 さて、図2は、最上段の「OPTICAL SYSTEM」タブを選択した状態になっていますが、他のタブ「DESIGN」「ANALYSIS」の内容も含めれば、全体として図5のような多彩なメニューが揃います。

図5 各タブに割り付けられたメニュー

3.1.2 光路の追加

 ここで、図2のレンズ図の中に 光線が何も表示されていないので追加してみます。その前に、説明図をコンパクトに収めるために、像面(Image Plane)をピントが合う位置まで移動して無駄なスペースを削減しておきます。図5の「DESIGN」タブの中の「Auto Focus」ボタンをクリックするだけで目的は達せられます(図6の ➀)。アプリは、光路が表示されていない状態でもジャスピン位置は把握できるようです。

 次に、図5の「ANALYSIS」タブを開き、「RAY TRACE」グループの中の「Chief Ray」「Marginal Ray」「Trace Ray」ボタンをクリックしてみます。それぞれのボタンに応じて図6の ➁,➂,➃ のように光線が追加されます(三つのボタンを同時に ON にすることもできますが、「Trace Ray」の状態と区別がつきません)。Marginal Ray は、レンズを通過することができる最も上側の光線と、最も下側の光線のことです。Chief Ray主光線)はそれらの(ほぼ)中央に位置する光線、Trace Ray は Marginal Ray に囲まれた範囲を均等に分割した複数本の光線です。

 さらに、「OPTICAL SYSTEM」タブの中の「VIEW」を「2D」から「3D」に切り替えれば、立体図として ➄,➅ のように表示することもできます。

図6 光路の追加

 しかし、2D 図はともかく、3D 図中の光線が平面的なものだけではチープです。そこで、「ANALYSIS」タブの中の「RAY TRACE」グループの「Configure Sampling Grid」をクリックします。すると、図7に示すようなウィンドウが現れます。左端の「Default」は z-y 平面上だけを通る光線です。2D 図のときにはこれさえあればほぼ十分ですが、このタイプには本数を変える手段がありません。本数を増やしたい場合には、無駄遣い感はありますが「Square」を選択して「Points per side」を大きくするしかありません。(このウィンドウだけは、設定変更のたびに「OK」ボタンを押す必要があり、そのたびにウィンドウが閉じてしまうので、繰り返し操作が面倒です。

 3D 図については「Default」以外の光線を使えば見映えが良くなりますが、適用例の図示は割愛します。

図7 Sampling Grid の種類

 以上で光路は描けました。しかし、まだ光軸に沿った平行光線だけで表現力が不十分です。そこで、光線を傾けたり、放射状の光線を利用するために、「OPTICAL SYSTEM」タブの「LIGHT SOURCE EDITOR」をクリックします。すると、GUI 画面の下半部の「Diagnostic Viewer」タブの隣に「Light Source Editor」タブが生成されます。そのままでは狭くて全貌が見えないので、dock から外して単独表示させたものを図8に示します。

 ここで使用するのは左半分の「Field Point Editor」です。「Type」のドロップダウンリストには、4つの選択肢「Field Angle」「Image Reference」「Object Reference」「Global」があります。2つ目以降を選択したときの入力欄の様子は赤枠内に示すとおりです。

図8 Light Source Editor の画面

 図6④ の「Trace Ray = ON」の状態にあるとき、図8の画面上で、下記の各操作をしたときの様子を図9に示しています。

  • 「Field Angle」で、入射する平行光線の傾斜を角度で指定して操作します(➀)。
  • 「Image Reference」で、同じく入射平行光線を、像面上の到達点の位置を指定して傾けます(➁)。
  • 「Object Reference」で、物体面上に指定した発光点から、放射状の光線を入射させます(➂)。
  • 「Global」で、三次元の x,y,z 座標で指定した発光点から、同じく放射状の光線を入射させます(➃)。

 なお、発光点や到達点を示す丸点マークは、図4の「Toggle field point display」を ON にしているときだけ表示されます。また、設定値の H(Hrizontal)や X はデフォルトの 0 のままとし、V(Vertical), Y, Z だけを操作してください。さもないと、光路が y-z 平面から外れて 2D 図では表示できなくなります。

 図8の左下にある「⊕」マークをクリックすれば、入射光を追加設定することもできます。この結果は、図9の ➄, ➅ に示しています。また、実像が得られない ➂ の状態でも、物体面を遠ざければ ➆ のように結像できるようになります。

図9 光路図の変形

3.1.3 収差の表示

光路図の説明は一通り済んだので、次は収差関係の説明に入ります。

 アプリの画面を、「LIGHT SOURCE EDITOR」をクリックした直後の状態に戻し(実際には戻す操作はできないので、最初からやり直す)たうえ、引き続き次の操作を行います。

 「ANALYSIS」タブの「ANALYSIS」グループにある全ボタン「Spot Diagram」「Ray Aberration」「Lens Distortion」「Chromatic Aberration」「Field Curvature」を順番にクリックします。これによって多くのタブが追加されるので、全部の中身が見えるように、3×3の形式で「すべて並べて表示」させます。さらに、アプリのウィンドウの右下にある「◀」マークをクリックして、右端に隠れていた全タブも開きます。

 この結果を図10に示しています。ここに、ほぼすべての役者が出揃いました。収差図として、スポットダイアグラムや、光線収差(横収差)、歪曲収差、色収差、像面湾曲のグラフも表示されています。

図10 収差関係の図なども加わった画面

 収差図は縮小されながらも全体が表示されていますが、右端にある操作欄は、初の御目見得にも拘わらず中身が殆ど分かりません。これらを全体が見えるように並べ直したものが図11です。

 ➀ の「Surface Parameters」は、図3の「Optical System Description」の補助的な操作部で、通常は白紙状態です。しかし、図2や図3の画面で 操作対象の surface 部をクリックすると、ここに、その面の詳細情報が表示されます。図11の例は、レンズの第5面が選択された状態です。Position には、その面が属するレンズ群の第1面からの相対位置が示されます。その他に、図3の諸元には含まれていないレンズ面の傾きや、非球面レンズのパラメータを設定することもできます。レンズ面ではなくレンズの肉部を選択したときには、この欄のタイトルは「Component Parameters」に変わります。

 ➁ の「Spot Diagram」では、スポットダイアグラムを描画させるために使用する光束の種類を設定します。図7の「Sampling Grid」と類似の機能で「Hexapolar」「Square」「Random」「Stratified」の4種類があります。スポットダイアグラムを見ながら操作できるので、ここには図形としては表示されません。

 ➂, ➃, ➄, ➅ の「Ray Aberration」「Lens Distortion」「Chromatic Aberration」「Field Curvation」は、それぞれの収差のグラフの描画条件を設定するものなのでしょうが、私には詳細は分かりません。

図11 図10の右側の操作欄の詳細

 ここでは、難しそうな収差のグラフはさておき、より面白そうなスポットダイアグラムだけをいじり倒して、もう少し遊んでみたいと思います。

 収差の様子については、これを補正した複合レンズよりも、収差まみれの単レンズをモデルにした方が分かりやすいと思います。そこで、図1の OPS6 を使い、光路図とスポットダイアグラムを対比させたものを図12に示しています。左の図では、光路全体の乱れはもちろん、複合レンズでは目立たなかった色違いの3本の光もはっきりと見分けられ、色収差が出ていることも分かります。

 光路のそれぞれの色(波長)は、図8の「Light Source Editor」の右半分にある「Wavelength Editor」で決められています。デフォルトで 486.1、587.6、656.3[nm] に設定されており、それぞれ、フラウンホーファー線の F線、D$_3$線(d線)、C線 の波長に相当しています。色としては、ほぼ 青, 黄, 赤 です。先に示したアッベ数 $\nu_d$ は、それぞれの波長における屈折率 $n_F$$n_d$$n_C$ を使って $\nu_d{=}(n_d{-}1)/(n_F{-}n_C)$ として定義されています。

図12 単レンズの、光路図とスポットダイアグラム

 図12の右の図は、同じ条件で描かせたスポットダイアグラムです。主光線の到達点を原点とし、一辺あたり11本の square 状の入射光線が、丸いレンズを通って像面上に達する位置を表しています。これにより、光路図では表せない二次元的な広がりも読み取ることができます。光路図の終端点からは、スポットダイアグラムの y 軸上の点群の位置が分かるだけです。

 しかし、スポットダイアグラムでも、図11の➁でのグリッド形式の選び方やスポットの数によって、受ける印象がかなり変わってきます。その様子を図13に示しています。

図13 グリッドの形式によるスポットダイアグラムの差異

 規則性がある ➀,➁ のようなグリッドでは、癖のある筋が現れるので判断を迷わせます。しかし、➂ のような単純な乱数でも、その都度の粗密に偏りがあって好感が持てません。➃ の Stratified によるスポットダイアグラムが最も自然な印象を受けます。そこで、このあとは ➃ を使って説明を続けることにします。

 その前に、利用上の裏技を披露しておきます。収差図の操作中に気になるのは、図の大きさを保つために、座標軸の目盛がオートスケールで勝手に変わることです。場合によっては便利な機能ですが、条件の変更に応じてスポットが拡大/縮小する様子を肌感覚で観察したいときには不便です。スケールを固定するボタンが欲しいところですが、どこにも見当たりません。その場合には次の手段が使えます。

スケールの固定方法:

  • 「スケールを固定したい座標面」の上にマウスカーソルを置く。
  • ホイールを回してスケールを変更し、ドラッグで座標軸を移動する。
  • 好みの状態になったら、マウスカーソルを座標面の外に出す。

 オートスケールに戻すには、座標面の右上の「表示の復元」マークをクリックします。なお、この方法を使っても、座標面の大きさや縦/横比は変えられません。

 ここで、OPS5 の複合レンズに戻り、図14に、画面の中心から周辺に向かうにつれて収差が増えていく様子を示しています。酷い収差のように見えますが、座標スケールは、単レンズの収差を表す図13とは全く異なります。感光面の周辺部では、よく「鳥が翼を広げたような」と表現される面白い形になっていきます。

図14 レンズ ops5 のスポットダイアグラム(Points per side = 41)

3.2 手入力による操作

 まともなレンズデータを使っていたのでは工数が増え過ぎて飽きてしまいます。要素数が少ない分光プリズムをモデルにして説明することにします。

 今まで開いていた「Optical System Designer」ウィンドウは「×」印をクリックして閉じ、新たなウィンドウを開き直してください。

 その後、図5の「OPTICAL SYSTEM」タブの「FILE」の中にある「New」をクリックします。すると、「Optical System Display」に空白の新しい座標面が現れ、「Optical System Description」には、「Object Plane」だけが表示された状態になります。

 ここで、「COMPONENTS」部分の「▼」をクリックして展開すると、図15左に示す6つのメニューが現れます。この中から、順に「Singlet」、「Image Plane」をクリックすると、「Optical System Display」には、同図右のように、ガラスブロックと像面だけの未加工の材料が準備されます。「Optical System Description」には、Surface1~3 の欄が追加され、それぞれにデフォルト値が設定されています。

図15 Singlet と Image Plane からプリズムの原型を作る

 ここから、ガラス材を少し厚くし、前面・後面を傾けてプリズムらしくして、像面も適度に遠ざけます。さらに、分光しやすい適度な屈折率とアッベ数を指定します。また、入射させる光は1本だけとし、分光後のスペクトルが美しくなるように、多くの波長の光を含ませます。

これらを満たすために、デフォルト値を次のように変更します。

  • 「Optical System Description」関連
    Surface1 ... Distance After = 5 → 10、Nd = 1.5 → 1.6、Vd = 50 → 30
    Surface2 ... Distance After = 0 → 200
    Surface3 ... Semi-Diameter = 10 → 18
  • 「COMPONENT PARAMETERS」関連
    Surface1 を選択した状態で、Tilt Angles X = 0 → 10
    Surface2 を選択した状態で、Tilt Angles X = 0 → -50
  • 「RAY TRACE」関連
    Chief Ray を選択します。
  • 「Field Point Editor」関連
    Field Angle V = 0 → 47
  • 「Wavelength Editor」関連
    左下の「⊕」マークを必要回数だけクリックして、波長の種類を11個まで増やします。
    各波長の値を、上から順に 420 から 670 まで 25 ステップで増やしながら書き換えます。
    色は設定した波長に合うように自動的に決めてくれます。

 このときの「Wavelength Editor」の様子を図16に、出来上がった分光スペクトルを図17に示します。

図16 多色を指定した Wavelength Editor

図17 分光プリズムによるスペクトル

 「こんなに細かい指示までは要らないよ!」とお叱りをいただきそうですが、まずは上記に従ってやってみてください。少しの数値の違いで、プリズムを通す以前から分光してしまったりするので意外に難しいものです。ただし、本来の使い方ではないためか、「Diagnostic Viewer」には警告が出まくりですから覚悟してください。

4. おわりに

 一通りの説明は済んだように見えますが、実は「DESIGN」タブ下にある「APERTURE CONSTRAINTS」については全く触れていません。選択できる項目は「Working F-number」「Diaphragm Radius」「Entry Pupil Radius」なので、入射する光線束の太さを制限する手段のように見受けられます。しかし、設定内容を変化させると、光線束が細くなるだけでなく、レンズの構造まで変化してしまいます。アプリ作者が想定している使用目的が読み取れません。

 その他にも不可解なことがいくつかありました。使ってみて戸惑ったことをここに備忘録としてメモしておきます。今まで、既成の光学ソフトは一度も使った経験がないので、個人的に違和感を持つものがあったとしても、それは世間では当然の慣習かもしれません。普段なら「バグ!」と言いたいところも、ここでは控えめに「あれっ?」と表現しておきます。まだまだ、ほんの駆け出しなので、思い違いも多そうです。

 個人的には、この光学アプリは R2026a の最も興味ある新機能と思っていました。しかし、MathWorks 社の R2026a リリースハイライト では一言も触れられていません。同社にとってはどんな位置づけなのでしょう。「自信をもってお勧めする!!」ほどのものではないということなのでしょうか?

4.1 あれっ? と思ったこと(アプリ編)

  • 主点や瞳面を示す点や線が、前面と後面で同じ色なので区別がつかない。
  • レンズ面は、太い黒線で描かれてはいるが、目立たないため口径が分かり難い。硝材の余計な贅肉が邪魔している。
  • 操作が気に入らないのか、ときどきフリーズする。
  • 像面の大きさに関係なくレンズ固有の特性が見たいのに、像面のサイズから外れる光線は勝手に無かったことにされる。
  • 入射光にまで色収差が現れることがある。
  • 各図の「最大化」のあと、「元に戻す」ボタンを2回押さないと元に戻らない。
  • 解放絞りの Semi-Diameter を丸めた値にすると、絞り解放時に瞳面の位置が飛ぶ。

4.2 あれっ? と思ったこと(コマンド編)

  • 3D 図は、事前に figure 番号を指定して開いておいても、そこには入らず、勝手に次番の figure を作って入る。
  • 3D 図は、↻マークが回り続けて描画が完了しないことがある。
  • 上記現象は、スクリプトの最初に clear の代わりに clear all とすれば現れない。
  • しかし、clear all 行自体がエラーと認識されることもある(アプリのウィンドウを閉じたあとに多い)。
  • 2D 図 でも、grid on は効かない。
  • 一般 figure 用の axis コマンドによる座標範囲の指定ができない(特殊コマンドが必要)
  • addGap コマンドは指定位置とは別の位置に Gap が入ることがある。

5. プログラム

プログラム(折り畳んであります)
% optical_exercise04.m

% Optical System Designer の練習用のレンズデータを生成。
% 
% 「標準フォーマット」のレンズデータから、「MATLAB の Optical System 
%  Designer アプリの GUI から読み取れる形式」のレンズデータファイルを
%  作成する。
% 
% さらに、GUI 画面からレンズデータを数値入力したい場合に備え、アプリの
%  フォーマットに合わせた数表をコマンドラインに出力する。
% 
% このプログラムを走らせると、書籍「レンズ設計のすべて - 光学設計の
%  神髄を探る」(辻貞彦 著、電波新聞社)から拾った5種類のレンズと、
%  自作の1種類の単レンズのデータが、アプリに適合した様式で Workspace
%  内に生成される。
% 
%  データ名 レンズ概要
%   ops1   No.58, Tessar型, f51.6mm, F2.8, 35mmフルサイズカメラ用
%   ops2   No.72, Ernostar型, f51.6mm, F2,     〃
%   ops3   No.84, Sonnar型, f51.6mm, F2,      〃
%   ops4   No.93, Gauss型, f51.6mm, F2,       〃
%   ops5   No.113, Gauss型, f51.6mm, F2,      〃
%   ops6   自作単凸レンズ, f51.6mm
% 
% なお、「MATLAB の Optical System Designer アプリの GUI から読み取れ
%  る形式」のフォーマットは、次の方法で推定したもの。MATLAB が保証し
%  たものではない。
%   アプリの GUI に手作業でレンズデータを数値入力してレンズ構成図を
%   描かせ、それを As Function で Export した function のスクリプト
%   を解読し、文法を推定した。

% ■■■ 光学アプリの opticalSystemDesigner は、閉じたままでも実行可能

clear
%clear all
           % ■■なぜか? 単なる clear だけでは、下記の異常が起こる。
           %  異常: view3d による描画が正常に完了しない。
           %      図は表示されても、〇型のマークがクルクルと
           %      回り続けて、ドラッグによる「3D図の視点の
           %      移動」も受け付けられない。
           % ■■しかし、なぜか? 稀に、この「clear all」を使うこと
           %      自体にエラーを警告されることもある。
           %      特に、「Optical System Designer」のウィンドウを
           %      閉じたあとの m ファイルの実行時に出やすい。
           % 
           % 原因不明なので、騙し騙し使用中。

close all

% レンズデータ
%  有効径   曲率半径  肉厚    屈折率  アッベ数

% No.058
FFo(1)=2.8;                 % 開放F値
Lnamex{1}=['No.58 Tessar型 51.6mm, F2.8'];
Surx{1}=[
     19.1      19.48717    4.50000    1.77250    49.60
     17.3      91.06909    4.00000    1.00000    Inf
     15.2     -62.93988    2.00000    1.72825    28.46
     14.1      17.94764    2.00000    1.00000    Inf
     14.18          Inf    2.00000    1.00000    Inf
     14.6     141.35119    1.00000    1.59270    35.31
     14.7      24.49046    4.00000    1.80440    39.59
     14.7     -37.36912    0.00000    1.00000    Inf
];

% No.072
FFo(2)=2;                   % 開放F値
Lnamex{2}=['No.72 Ernostar型 51.6mm, F2'];
Surx{2}=[
     29.0      22.09251    6.00000    1.65844    50.88
     27.5     123.19285    0.10000    1.00000    Inf
     22.4      21.27789    4.00000    1.63854    55.38
     20.1      32.11702    2.50000    1.00000    Inf
     19.2    -424.31820    1.20000    1.76182    26.52
     17.0      15.47789    4.00000    1.00000    Inf
     16.88          Inf    4.96104    1.00000    Inf
     16.6      58.13366    2.50000    1.72342    37.93
     16.4     -52.72323    0.00000    1.00000    Inf
];

% No.084
FFo(3)=2;                   % 開放F値
Lnamex{3}=['No.84 Sonnar型 51.6mm, F2'];
Surx{3}=[
     28.7      32.73326    3.80000    1.69680    55.53
     27.4      97.63145    0.19000    1.00000    Inf
     23.5      17.57712    4.50000    1.69680    55.53
     22.0      36.68896    3.30000    1.48749    70.21
     20.5    2255.56502    1.00000    1.64769    33.79
     16.8      12.10293    4.50000    1.00000    Inf
     16.40          Inf    1.20000    1.00000    Inf
     16.2    -108.10542    6.00000    1.53172    48.84
     15.3      27.87691   10.00000    1.77250    49.60
     19.8     -64.34508    0.00000    1.00000    Inf
];

% No.093
FFo(4)=2;                   % 開放F値
Lnamex{4}=['No.93 Gauss型 51.6mm, F2'];
Surx{4}=[
     32.4      28.45336    5.00000    1.65844    50.88
     31.0      81.93668    0.10000    1.00000    Inf
     24.9      17.86823    6.50000    1.64850    53.02
     22.0     244.84648    1.30000    1.64769    33.79
     16.9      11.87418    6.50000    1.00000    Inf
     15.94          Inf    6.50000    1.00000    Inf
     14.6     -13.85226    1.00000    1.64769    33.79
     17.1     -49.52634    4.00000    1.64850    53.02
     19.8     -19.13128    0.10000    1.00000    Inf
     23.9     667.74212    4.50000    1.65844    50.88
     25.0     -29.07016    0.00000    1.00000    Inf
];

% No.113
FFo(5)=2;                   % 開放F値
Lnamex{5}=['No.113 Gauss型 51.6mm, F2'];
Surx{5}=[
     38.5      41.22676    5.50000    1.80610    40.92
     36.7     141.20948    0.10000    1.00000    Inf
     28.7      20.82621    9.00000    1.71700    47.92
     22.9   -1427.55747    1.32000    1.71736    29.51
     16.2      13.55312    6.45000    1.00000    Inf
     15.04          Inf    6.45000    1.00000    Inf
     15.1     -18.95597    1.32000    1.71736    29.51
     19.4      86.65094    9.00000    1.71700    47.92
     25.6     -24.97014    0.10000    1.00000    Inf
     33.0      70.76937    5.50000    1.80610    40.9
     33.7     -86.13304    0.00000    1.00000    Inf
];

% 単凸レンズ
FFo(6)=2;
Lnamex{6}=['単凸レンズ 51.6mm'];
Surx{6}=[
     25.0      50.0000     3.58140    1.60000    29.51
     25.0     -79.1187     0.00000    1.00000    Inf
];

kDiaphragm=1.0;    % 絞りの直径を開放時の kDiaphragm 倍にする。

figure(1)
hfig=gcf;

for nc=1:6         % レンズのモデル番号

  % Define Optical System ==============================
  ops{nc} = opticalSystem( ...
        Name = ['ops' num2str(nc)], ...
        Wavelengths = [4.861340e+02, 5.875618e+02, 6.562810e+02], ...
        PrimaryWavelengthIndex = 2);

  % Define Object Plane ==============================
  shape = optics.shape.Circular(10);
  objPlane = optics.component.ObjectPlane( ...
        Name = 'Object Plane (物体面)', ...
        Position = [0, 0, -5], ...
        TiltAngles = [0, 0, 0], ...
        Shape = shape);
  ops{nc}.ObjectPlane = objPlane;

  % 元になるレンズデータの取り込み
  Sur=Surx{nc};
  kk=size(Sur,1);        % 境界面数
  gap=Sur(:,3)';         % 境界面間の長さ

  isDiaphragm=[];        % 絞り面の識別用フラグ(初期化)
  Com=[];                % 各境界面が所属する Component(群)の
                         %  番号を初期化

  % 各境界面の所属 Component 番号 と 絞り面の識別用フラグ を設定
  N_com=1;               % 所属する Component 番号の初期値
  for n=1:kk
    if ~(   (Sur(n,2)==0 | isinf(Sur(n,2))) ...
          & Sur(n,4)==1.0 ...
          & (n>=2 && Sur(n-1,4)==1.0) ...
        )                % 絞り面ではないとき
      Com(n)=N_com;           % 所属 Component 番号
      isDiaphragm(n)=false;   % 絞り面の識別用フラグ
    else                 % 絞り面のとき
      Com(n)=N_com;
      isDiaphragm(n)=true;
    end
    if Sur(n,4)==1       % 面の後が空気なら、
      N_com=N_com+1;     %  当 Component の終端ゆえ、次の番号は +1。
    end
  end

  for ncom=1:max(Com)             % 各 Component を順番に処理
    Ns=find(Com==ncom);
    for nn=Ns(1):Ns(end)          % 処理中の Component 内にある
                                  %  境界面番号。
      if isDiaphragm(nn)==true;   % 絞り面のとき
        % Define Diaphragm ============================
        addDiaphragm(ops{nc}, ...
            Name = ['S' num2str(nn)], ...
            SemiDiameter = kDiaphragm*Sur(nn,1)/2, ...
            DistanceToNext = Sur(nn,3));
        ops{nc}.Components(end).Name = ['C' num2str(ncom)];

      else                        % レンズ面のとき
        if nn~=Ns(end)            % Component の終端ではないとき
          % Define Material
          mat = opticalMaterial( ...
              [Sur(nn,4) Sur(nn,5)], ...
              Name = '');

          % Define Surface ============================
          addRefractiveSurface(ops{nc}, ...
              Name = ['S' num2str(nn)], ...
              Radius = Sur(nn,2), ...
              SemiDiameter = Sur(nn,1)/2, ...
              Material = mat, ...
              DistanceToNext = Sur(nn,3));

        else                      % Component の終端のとき
          
          % Define Surface ============================
          addRefractiveSurface(ops{nc}, ...
              Name = ['S' num2str(nn)], ...
              Radius = Sur(nn,2), ...
              SemiDiameter = Sur(nn,1)/2);

          % Update Component's Name, Position and Tilt Angles
          ops{nc}.Components(end).Name = ['C' num2str(ncom)];
          ops{nc}.Components(end).Position = ...
                                          [0, 0, sum(gap(1:Ns(1)-1))];
          ops{nc}.Components(end).TiltAngles = [0, 0, 0];

          Sn=length(Ns);
          sh = optics.shape.Shape.empty(Sn,0);
          for nnn=1:Sn
            sh(nnn) = optics.shape.Circular(Sur(Ns(1)+nnn-1,1)/2);
          end
          ops{nc}.Components(end).Shape = sh;

          % Add Gap(次の Component までの長さ)
          ops{nc}.addGap(Sur(nn,3));

        end        % end of 「if nn~=Ns(end)」
      end        % end of 「isDiaphragm(nn)==true」
    end        % end of 「for nn=Ns(1):Ns(end)」
  end        % end of 「for ncom=1:max(Com)」

  % Add Gap(レンズの最後面から Image Plane までの長さ)
  ops{nc}.addGap(50);

  % Define Image Plane ====================
  addImagePlane(ops{nc}, ...
      Name = 'Image Plane (像面)', ...
      SemiDiameter = norm([24 36])/2);

  subplot(3,3,nc)
  hv1 = view2d(ops{nc},ZLim=[-10 100]);     % レンズの構成図を描画

  % テスト用の入射光束を描き込む
  %  (ただし、import 用レンズデータには入らない)
  rays1 = traceRays(ops{nc},Wavelengths=587.5618);
                           % 光軸に平行な光線束を定義する。
  fp = fieldPoint(Angles=[15 0]);
                           % 斜め方向を設定(下方15度、左右の中央)
  rays2 = traceRays(ops{nc},FieldPoints=fp,Wavelengths=587.5618);
                           % 斜めに入射する平行な光線束を定義する。
  addRays(hv1,rays1)       % 図中に、光線束を重ね描き

  % レンズ構成を手入力する場合の数値データ
  %  (コマンドラインに表形式で出力)
  suf_nm = ops{nc}.SurfaceTable.Index;
  suf_nm = [repmat('Surface',length(suf_nm),1) num2str(suf_nm)];
  radius = ops{nc}.SurfaceTable.Radius;
  sem_di = round(ops{nc}.SurfaceTable.Shape,5);
  ds_aft = round(diff(ops{nc}.SurfaceTable.Position(:,3)),5);
  ds_aft(end+1) = 0;
  % ======================
  materi = ops{nc}.SurfaceTable.MaterialName;
  % テーブル出力の文字列が「"」で囲まれると煩わしいので、
  %  セル配列から文字行列に変換することで「"」が出ないようにする。
  Ma=repmat(' ',size(materi,1),20);
  Nmax=0;
  for ns=1:size(materi,1)
    Nchar=size(materi{ns},2);
    Ma(ns,[1:Nchar])=materi{ns};
    Nmax=max([Nchar Nmax]);
  end
  Ma(:,[Nmax+1:end])=[];
  % ======================
  Nd     = ops{nc}.SurfaceTable.NdVd(:,1);
  Vd     = ops{nc}.SurfaceTable.NdVd(:,2);
  variableNames = ["Name","Radius","Semi-Diameter", ...
                             "Distance After","Material","Nd", "Vd"];
  prescriptionTable = table(suf_nm,radius,sem_di,ds_aft,Ma,Nd,Vd, ...
                                        VariableNames=variableNames);
  disp([' ' newline 'ops' num2str(nc)])
  disp(prescriptionTable)

end        % enf of 「for nc=1:6」

% 光学アプリの opticalSystemDesigner は、なぜか ops{1}, ops{2}, ... 
%  形式のファイルは import できないので、ops1, ops2, ... と改名する。
%  これには、「eval」コマンドを使わざるを得ない。

for m=1:size(ops,2)
  eval(['ops' num2str(m) '=ops{m};']);
end

% figure のトリミング
trim_and_expand_figure_margin(hfig,[-20 -163 -53 -53]);

% figure を画像ファイルとして出力
basename=mfilename('fullpath');     % このプログラムのファイル名
hfig.Color=[0.96 0.96 0.96];
%print(hfig,'-dpng',[basename '_01.png'],'-r600')


% ====================================
% figureの上下左右の余白調整
% ====================================

function trim_and_expand_figure_margin(hf,KK)

  % 注: figure に legend が含まれているとエラーになる。
  %    legend は、この関数を実行後に作成すること。
  %    tiledlayout の図もダメ。subplot ならOK。

  % 【入力】
  %  hf:  余白調整したいfigureのハンドル
  %  KK:  余白の変更量から成る配列。 単位は[pixel]。
  %        ([上余白 下余白 左余白 右余白]。増量は+、減量は-)
  % 【出力】
  %  返り値は無し

  posf=hf.Position;            % 余白変更前のfigureのposition
                               %  [左端 下端 幅 高さ]、単位は[pixel]
  naxe=size(hf.Children,1);    % figure内のChildren(axesやcolorbar)
                               %  などの総数。

  % 全Childrenのposition行列を作成
  %  各行がそれぞれのChildrenに対応。
  %  各列は[左端 下端 幅 高さ]、単位は[normalized]。

  Posa=[];                     % position行列の初期値
  for n=1:naxe
    axes(hf.Children(n))
    hn(n)=gca;
    Posa=[Posa ; hn(n).Position];
  end

  % 各Childrenの、余白変更前のfigure内でのpixel単位でのPositionを計算
  Posp(:,[1 3])=Posa(:,[1 3])*posf(3);
  Posp(:,[2 4])=Posa(:,[2 4])*posf(4);
  
  % 余白変更後のfigureのpixel単位でのPositionを計算。
  posfx=posf+[-KK(3) -KK(2) KK(3)+KK(4) KK(1)+KK(2)];
  
  % 余白変更後のfigure内での各Childrenのpixel単位でのPositionを計算
  Pospx(:,1)=Posp(:,1)+KK(3);
  Pospx(:,2)=Posp(:,2)+KK(2);
  Pospx(:,3)=Posp(:,3);
  Pospx(:,4)=Posp(:,4);
  
  % 余白変更後の各Childrenのnormalized単位でのPositionを計算する。
  Posax(:,[1 3])=Pospx(:,[1 3])/posfx(3);
  Posax(:,[2 4])=Pospx(:,[2 4])/posfx(4);
  
  % figureとChildrenのpositionを更新
  hf.Position=posfx;
  for nn=1:naxe
    hn(nn).Position=Posax(nn,:);
  end

end
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?