Moiz's journal

プログラミングやFPGAなどの技術系の趣味に関するブログです

【技術書典6】Colabから印刷所に出せる原稿を作るワークフロー

Colabから印刷所に出せる原稿を作るワークフロー

はじめに

先日行われた技術書典6で「PythonとColabでつくる-ゼロから作るRAW現像」をid:uchan_nosさんのブースで頒布していただき、用意していた全100部を完売することができました。 ひと月前に部数を相談していたときには50部売れるかなと心配していたのが嘘のようです。 id:uchan_nosさんと@akachochinさんにはいくら感謝しても足りません。

さてイベントも終わり一段落したところですが、今回の原稿作成はちょっと変わったワークフローだったのでまとめておこうかと思います。

Colabから原稿へ移す際の問題点

今回の本はタイトルからもわかるとおり、Colab上で実行できるコードを使う事により、読者のハードルを下げるというのが特徴の一つです。 書籍の方ではColabで行った処理を、結果含めてそのまま再現しなくてはなりません。

当然コードは全部実行可能、出力結果はColabの物と同一でなくてはなりませんが、これが意外と問題になります。

もともとこの同人誌は当ブログで書いた連続記事が元になっています。ブログの方ではJupyter Notebookを使いましたが、実行可能なコードと解説という組み合わせは同人誌版とほぼ同じです。 しかしブログ版を書いたときにはコードはJupyterからのコピペに頼っていたために、

  • コピーミス
  • バージョンが違うコードが混在する
  • 後からの編集で原稿とのミスマッチがおきる

という問題がおきました。特に最初の2つはコードの実行ができなくなる可能性があるので大問題です。

また今回は終盤結構な数の数式が出てくるので、編集の手間を考えるとできるだけ手戻りは防ぎたいところです。

Colabから原稿へのワークフロー

そこで今回はこのようなワークフローを選択しました。

  1. ブラウザ上でColabでプログラム作成。GitHubに保存。
  2. 同Colabで原稿作成(図およびLaTex形式の数式入り)。GitHubに保存。
  3. ローカル環境で、nbconvertによりColabのノートブックからPDFに変換。
  4. 一次校正
  5. nbconvertによりColabからマークダウン形式への変換
  6. 自作スクリプトPython)による画像リンクの修正
  7. マークダウンからPDFに変換
  8. 二次校正
  9. md2reviewでMark DownからRe:VIEW形式に変換
  10. 壊れてしまったLaTex形式数式を修正
  11. レイアウトの調整
  12. Re:VIEWによるPDF原稿の出力
  13. 最終校正
  14. GitHub上の原稿の調整(公開するため)
  15. 完成

また各ステップでMakefileをつくり、できるだけ単純作業は自動化するようにしました。 具体的にはColabで変更を加えると、Make一発で9のRe:VIEW形式への変換まで実行されるようにしました。

ここで4の校正は数回、8のニ時校正も2〜3回行っていますが、Colabで変更してから校正用PDFにするまでは自動なので負担はそれほど大きくありませんでした。

一次と二次の校正の回数が多いのは、Re:VIEW形式にしたあとでは手作業が多いため、これ以降はColabには戻りたくないので、事前にできるだけ完成度を上げておく必用があったからです。

解説と振り返り

最初の目論見では、3のPDFの完成度が高いようならそのまま本にしてしまおうと思っていたのですが、実際に出力されたPDFは間延びしたもので、また、印刷原稿に必用なトンボやノンブルもなく、印刷所にだす原稿には使えなさそうなクオリティーでした。ただ、テキストやColabのコードの確認には十分だったので何回かPDF化してその上で校正しました。

次の5のマークダウン形式への変換と7のPDFの変換では、GitHub上に用意した画像ファイルがPDF化されないという問題がありました。これは6にて、ローカルのリポジトリーのパスに置き換えるスクリプトを実行することで回避しました。

9の「壊れてしまったLaTex形式数式」の修正というのは、md2reviewがうまく認識できなかったLaTex数式を通常のマークダウンと認識してしまう部分が何箇所かあったので、それを修正する作業です。これは自動化したかったのですがうまくいきませんでした。また、md2reviewの調整でうまくいくのかもしれません。1

10以降は通常のRe:VIEWによる技術書執筆とあまり変わらないと思います。今回このあたりは基本的に手作業でした。23

原稿ができたあとでみてみると、Colabノートブックから、マークダウン形式、Re:VIEW形式、と2段階の返還をしているのが気になります。 nbconvertはLaTex形式への変換もできるので、そうしておけば1回の変換ですんだのではないかと思いますが、私自信がLaTexについてあまり知らないのでこのようなフローになってしまいました。

また、ステップ10以降が手作業がメインになってしまっている点も気になります。理想はColabから最終Re:VIEWへ完全自動化だったのですが、なかなかそううまくは行かないようです。 このあたりは次回への課題です。

環境に関しては、今回は原稿部分は図の作成含めてすべてUbuntu18.04上で行いました。表紙の作成はWindows上で行っています。

校正には印刷の他にiPad Proを使用しましたが非常に便利でした。

まとめ

以上、今回「ゼロから作るRAW現像」の原稿を作成するにあたって利用したワークフローを紹介しました。

ColabやJupyterといったPythonインタラクティブノートブックで実行したものを原稿にする際にしか参考にならないニッチな情報ですが、もともと技術書典というのはそういうニッチな物を扱う点に価値があるのかもしれませんので、ここで関連記事として公開させていただきます。

最後に

【技術書典6】で頒布した「PythonとColabでできる - ゼロから作るRAW現像」の書籍版・PDF版ををBOOTHにて販売しています。 書籍+PDF版は2200円プラス送料、PDF版は1200円です。

moiz.booth.pm

【技術書典6】キヤノンD70のRAW画像のPythonによる現像

キヤノンD70のRAW画像のPythonによる現像

このページについて

このページでは「技術書典6」にて配布する「PythonとColabでできる - ゼロからできるRAW現像」のデモとして、書籍ではあつわかないキヤノンのRAWファイルの現像を行ってみます。

あくまでデモという目的なのでパラメータは最適化していませんし、一部機能はオフにしています。

このデモの内容をColabで実際に実行するには次のリンクからアクセスしてみて下さい。

colab.research.google.com

準備

まずライブラリーのインストールと、モジュールのインポート、画像の読み込みを行います。内容については書籍を参照ください。

# rawpyとimageioのインストール
!pip install rawpy;
!pip install imageio;

# rawpy, imageio, numpuy, pyplot, imshowのインポート
import rawpy, imageio
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow

# 前節までに作成したモジュールのダウンロードとインポート
!if [ ! -f raw_process.py ]; then wget https://github.com/moizumi99/camera_raw_processing/raw/master/raw_process.py; fi
from raw_process import simple_demosaic, white_balance, black_level_correction, gamma_correction
from raw_process import demosaic, defect_correction, color_correction_matrix, lens_shading_correction
from raw_process import noise_filter, apply_matrix, edge_enhancement, tone_curve_correction, advanced_demosaic

# 日本語フォントの設定
!apt -y install fonts-ipafont-gothic
plt.rcParams['font.family'] = 'IPAPGothic'

日本語が文字化けしている場合は、以下のセルから冒頭の#を削除した上で実行して、Rntime->Restart Runtimeを選択し、その後ページはじめから実行してみて下さい。

# もし日本語が文字化けしている場合以下の3行の行頭の#を削除して実行後、
#import matplotlib
#target_dir = matplotlib.get_cachedir()
#! rm {target_dir}/*.json
# その後Runtime->Restart Runtime選択してページ全体を再実行

今回はフリーのRAWファイルサンプルを公開しているRAWSMAPLES.CHからデータをダウンロードします。

使うのはCANON EOS70D のデータで、ファイル名はRAW_CANON_EOS70D.CR2です。

# 画像をダウンロードします。
! wget http://www.rawsamples.ch/raws/canon/RAW_CANON_EOS70D.CR2

自分で撮影した画像を使用する場合は以下のセルからコメントを取り除きアップロードします。

#from google.colab import files
#uploaded = files.upload()
# RAWファイルの名前。
# アップロードしたファイルを使う場合はその名前に変更。
raw_file  = "RAW_CANON_EOS70D.CR2"
raw = rawpy.imread(raw_file)

exiftoolを使ってRAWファイルの情報を見てみましょう。

! apt install exiftool
! exiftool RAW_CANON_EOS70D.CR2
ExifTool Version Number         : 10.80
File Name                       : RAW_CANON_EOS70D.CR2
Directory                       : .
File Size                       : 23 MB
File Modification Date/Time     : 2019:04:07 15:52:05+00:00
File Access Date/Time           : 2019:04:07 15:52:33+00:00
File Inode Change Date/Time     : 2019:04:07 15:52:05+00:00
File Permissions                : rw-r--r--
File Type                       : CR2
File Type Extension             : cr2
MIME Type                       : image/x-canon-cr2
Exif Byte Order                 : Little-endian (Intel, II)
Image Width                     : 5472
Image Height                    : 3648
Bits Per Sample                 : 8 8 8

 以下略

沢山の情報が得られます。

まず、ブラックレベルは2049です。

Per Channel Black Level : 2049 2049 2049 2049

また、次の行からホワイトレベル(白をしめす基準値)11765と思われます。

Normal White Level : 11765

Bayerのパターンは「赤・緑」「緑・青」のようです。

CR2 CFA Pattern : [Red,Green][Green,Blue]

残念ながらカラーマトリクスの値がわかりません。もしかするとこのカメラでは単純なカラーマトリクスではない方式で色補正を行っているのかもしれません。 今回は適当な値を設定して使う事にします。

まずはRAWPYの機能を使って最終的な画像を見ておきましょう。

# RAWPYの機能で現像処理。
# use_camera_wbはホワイトバランスにカメラの値を使う指定。
rgb = raw.postprocess(use_camera_wb=True)
plt.title("RAWPYによる現像")
plt.imshow(rgb)
plt.axis('off')
plt.show()

f:id:uzusayuu:20190408035709p:plain

これは、鳥の餌かなにかでしょうか?

ちょっと面食らいましたが気を取り直して、RAWデータの情報を見てみましょう。

print(raw.sizes)
ImageSizes(raw_height=3708, raw_width=5568, height=3670, width=5496, top_margin=38, left_margin=72, iheight=3670, iwidth=5496, pixel_aspect=1.0, flip=6)

画像サイズは5568x3670のようです。約20Mです。

Bayerパターンを確認しましょう。

print(raw.raw_pattern)
[[0 1]
 [1 2]]

左上が赤、右下が青、というそれ以外が緑というパターンで、先程確認したEXIFの情報と一致しています。

ホワイトバランスを確認します。

print(raw.camera_whitebalance)
[2036.0, 1024.0, 1739.0, 1024.0]

これはEXIFの「WB RGGB Levels As Shot」に対応しているようです。

それではRAW現像に入りましょう。

RAW画像データをNumpy配列に読み込みます。

raw_array = raw.raw_image
h, w = raw_array.shape

それでは処理を行いましょう。

今回はデモなのでシェーディング補正や欠陥画素補正、ノイズリダクション、トーンカーブ処理は省略しています。

EXIFでわからなかったカラーマトリクスの値として(1.5, -0.25, -0.25, -0.25, 1.5, -0.25, -0.25, -0.25, 1.5)という物を仮の値としていれています。

また、ホワイトレベルで正規化した場合暗い画像になってしまうので、x2のデジタルゲインを与える事にしました。

処理の詳細については技術書典6で配布する書籍「PythonとColabでできる - ゼロからできるRAW現像」をご覧ください。

blc, pattern = raw.black_level_per_channel, raw.raw_pattern
blc_raw = black_level_correction(raw_array, blc, pattern)
gains, colors = raw.camera_whitebalance, raw.raw_colors
wb_raw = white_balance(blc_raw, gains, colors)
dms_img = advanced_demosaic(wb_raw, pattern)
# カラーマトリクス。EXIFから得られたもの。
color_matrix = np.array(
    [1.5, -0.25, -0.25, -0.25, 1.5, -0.25, -0.25, 0.25, 1.5])
ccm_img = color_correction_matrix(dms_img, color_matrix)
# ホワイトレベル。EXIFから得られた値。
white_level = 11765.0
# デジタルゲイン
digital_gain = 2
gmm_img = gamma_correction(ccm_img / white_level * digital_gain, 2.2)
sigma, edge_intensity = 2, 0.25
shp_img = edge_enhancement(gmm_img, sigma, edge_intensity)

表示してみます。

# 最終画像表示
plt.figure(figsize=(8, 8))
plt.imshow(shp_img)
plt.axis('off')
plt.title(u"最終画像")
plt.show()

f:id:uzusayuu:20190408035610p:plain

先程のRAWPYによる現像と似た画像が得られました。

今回はパラメータを簡易的なものにしたので色合いがことなる、コントラストが違う、などの点がみられます。

このあたりはカラーマトリクスの値の変更、トーンカーブ処理などで改善できると思われます。

まとめ

今回は技術書典6で配布する書籍「PythonとColabでできる-ゼロから作るRAW現像」のデモとして、キヤノンのカメラEOS70DのRAW画像ファイルを現像してみました。

f:id:uzusayuu:20190331124210j:plain:w200

書籍ではRAW画像とは何か?という疑問に答えるところから始まり、各ステップで行われる処理を基本的なところから説明し、実際に実行可能なコードを使って処理を行ってみます。

技術書典について

公式ウェブページによる紹介は以下の通りです。

新しい技術に出会えるお祭りです。

技術書典は、いろんな技術の普及を手伝いたいとの想いではじまりました。

技術書を中心として出展者はノウハウを詰め込み、来場者はこの場にしかないおもしろい技術書をさがし求める、技術に関わる人のための場として『技術書典』を開催します。

  • 日時: 2019/04/14 (日) 11:00〜17:00 (一般参加は11時~13時のみ有料)
  • 開催場所: 池袋サンシャインシティ2F 展示ホールD(文化会館ビル2F)

配布情報

  • ブース: う38「bitnos
  • 書名:「PythonとColabでできる-ゼロから作るRAW現像」
  • フォーマット
    • 書籍版:164ページ(内12ページフルカラー+表紙含む)、予価2000円
    • PDF版:フルカラー164ページ(表紙含む)、予価1000円

書籍版の方はカラーページを含むために若干高めになってしまいましたので、特典としてPDF版ダウンロード権を添付します。

またPDF版は、同等品をイベント後BOOTHでの販売を予定しています(値段など変わる可能性があります)。

追記

現在はBOOTHにて入手可能です。書籍+PDF版は2200円プラス送料、PDF版は1200円です。

moiz.booth.pm

サークルbitnosについて

「う38」のサークルbitnosは低レイヤーを中心としたソフトウェアのサークルです。

今回は新刊「Local APICタイマー入門」を配布します。 以下、著者の@uchan_nosさんによる紹介です。

現代のPCにはいくつものタイマーが搭載されています。本書は現代のPCタイマーの主役であるLocal APICタイマーを中心にしつつも、ACPI PMタイマー、TSCUEFI RTCの使い方も紹介します。また、x86-64における割り込みの設定についても詳しく解説します。

他に以下の既巻も頒布します。

  • Linuxカーネルモジュール自作入門」
  • システムプログラミングハンドブック」
  • 「USB 3.0 ホストドライバ自作入門」
  • C++でできる!OS自作入門」

【技術書典6】 画像処理は低レイヤー

画像処理は低レイヤー

はじめに

前回の記事で告知したとおり、4月14日に行われる技術書オンリーイベント『技術書典6』にて『う38』のサークルbitnosのご厚意で「PythonとColabでできる - ゼロから作るRAW現像」を配布します。

サークルbitnosは低レイヤーを中心とするソフトウェアのサークルなのですが、そこで画像処理の書籍を配布することに意外に思われる方もいらっしゃるかもしれません。 実際に今回配布するに至った理由の多くはサークル主のid:uchan_nosさんのご好意という面が大きいのですが、ジャンルだけをとっても実は私にはあまり違和感はありません。

RAW現像の処理はカメラ画像処理

確かにPC上で実行する市販のRAW現像ソフトを思い浮かべると、そういった処理は低レイヤーとは程遠いと言えますが、RAW現像で行われる処理はもともとカメラ内で行われていた画像処理です。

もちろんPCで行うためにより高度な処理が行える、マニュアル的な補正ができる、カメラメーカー以外が提供するソフトウェアが使える、といった違いはありますが、処理の内容的にはカメラ画像処理のエミュレーションという言い方もできると思います。

スマートフォンのカメラ機能

それではカメラ内の画像処理の例として、身近なスマートフォンのカメラ機能を考えてみましょう。

まず画像処理については次の方法が考えられます。

  1. CPU・GPUなどの汎用プロセッサで行う
  2. DSPなどの画像処理に特化した専用プロセッサで行う
  3. 専用のハードウェア回路で行う

1や2の場合は、画像処理自体はファームウェアまたはドライバーとして書かれます。

3の場合は画像処理はハードウェアの一部なのでCPUやGPUと同じレイヤーです。 ただし、制御処理や画像処理の一部はドライバーソフトウェアとして提供される事になります。

カメラ機能を実現するには他に、

などが必用です。

カメラコントロールは各種プロセッサ上で実行されますが、これはドライバーの領分です。

後処理はどこで行われるかシステムによって違いますが、GPUDSPで行う場合はファームウェアやドライバー上で行うことになります。

エンコードは専用ハードウェアやアクセラレータで行われることが多いですが、この場合画像処理部分と同様にハードウェア・ファームウェア・ドライバーといった低レイヤーで実現されます。

このように、スマートフォンのカメラ機能のほとんどの部分は低レイヤー・ソフトウェアおよび更に下の層であるハードウェアで実現されています。

そんなわけで、RAW現像は低レイヤーソフトウェアやハードウェアと関連が非常に深いのです。

最後に

とうとう技術書典6も1週間後の4月14日にせまってきました。

都合のつく方はどうぞご来場して、もし気が向いたらぜひサークルbitnosをお訪ね下さい。

技術書展の開催情報

  • 日時: 2019/04/14 (日) 11:00〜17:00 (一般参加は11時~13時のみ有料)
  • 開催場所: 池袋サンシャインシティ2F 展示ホールD(文化会館ビル2F)

書籍配布情報

  • ブース: 「う38」
  • 書名:「PythonとColabでできる-ゼロから作るRAW現像」
  • フォーマット
    • 書籍版:164ページ(内12ページフルカラー+表紙含む)、予価2000円
    • PDF版:フルカラー164ページ(表紙含む)、予価1000円

サークル情報

  • サークル: bitnos
  • サークル主催:id:uchan_nosさん
  • ジャンル: オペレーティングシステムを自作する際の要素技術の解説本など
  • 新刊書籍: 「Local APICタイマー入門」
    • 現代のPCに搭載されている様々なタイマーの使い方を紹介します

f:id:uzusayuu:20190331124210j:plain:w300

追記

現在はBOOTHにて入手可能です。書籍+PDF版は2200円プラス送料、PDF版は1200円です。

moiz.booth.pm


  1. オートホワイトバランス、オート露出、オートフォーカス

【技術書典6】「PythonとColabでできる-ゼロから作るRAW現像」を出します(書籍版/ダウンロード版)

はじめに

第六回目を迎える 技術書オンリーイベント 「技術書典6」にて『う38』のサークルbitnosにて「PythonとColabでできる-ゼロから作るRAW現像」(以下「ゼロから作るRAW現像」)を配布します。

f:id:uzusayuu:20190331124210j:plain:w300

技術書典について

公式ウェブページによる紹介は以下の通りです。

新しい技術に出会えるお祭りです。

技術書典は、いろんな技術の普及を手伝いたいとの想いではじまりました。

技術書を中心として出展者はノウハウを詰め込み、来場者はこの場にしかないおもしろい技術書をさがし求める、技術に関わる人のための場として『技術書典』を開催します。

  • 日時: 2019/04/14 (日) 11:00〜17:00 (一般参加は11時~13時のみ有料)
  • 開催場所: 池袋サンシャインシティ2F 展示ホールD(文化会館ビル2F)

配布情報

  • ブース: う38「bitnos
  • 書名:「PythonとColabでできる-ゼロから作るRAW現像」
  • フォーマット
    • 書籍版:164ページ(内12ページフルカラー+表紙含む)、予価2000円
    • PDF版:フルカラー152ページ(表紙含む)、予価1000円

書籍版の方はカラーページを含むために若干高めになってしまいましたので、特典としてPDF版ダウンロード権を添付します。

サークルbitnosについて

「う38」のサークルbitnosは低レイヤーを中心としたソフトウェアのサークルです。

今回は新刊「Local APICタイマー入門」を配布します。 以下、著者の@uchan_nosさんによる紹介です。

現代のPCにはいくつものタイマーが搭載されています。本書は現代のPCタイマーの主役であるLocal APICタイマーを中心にしつつも、ACPI PMタイマー、TSCUEFI RTCの使い方も紹介します。また、x86-64における割り込みの設定についても詳しく解説します。

他に以下の既巻も頒布します。

  • Linuxカーネルモジュール自作入門」
  • システムプログラミングハンドブック」
  • 「USB 3.0 ホストドライバ自作入門」
  • C++でできる!OS自作入門」

配布する本について

本書「ゼロから作るRAW現像」ではカメラのRAW画像現像処理をスクラッチから作成・解説します。 PythonとColabを使うので読者はブラウザだけで、Bayerデータからフルカラー画像までのカメラ画像処理を低レベルから理解し実行することができます。

内容的にはこのブログで以前書いた「ゼロから作るRAW現像」を大きく再構成してわかりやすくし、実行環境を設定のいらないColabに移したものです。 ブログでは説明が足りないところ飛んでいるところ多数ありましたが、そういった点を大きく改善しました。

目次

  • はじめに

    • この本について
    • 環境について
    • 書籍の内容について
  • 第 1 章 カメラ画像処理について

    • カメラ画像処理について
  • 第2章 基本的な処理

    • 2.1 準備
    • 2.2 簡易デモザイク処理
    • 2.3 ホワイトバランス補正
    • 2.4 ブラックレベル補正
    • 2.5 ガンマ補正
  • 第3章 重要な処理

    • 3.1 この章について
    • 3.2 線形補間デモザイク
    • 3.3 欠陥画素補正
    • 3.4 カラーマトリクス補正
    • 3.5 シェーディング補正
  • 第4章 画質を良くする処理

    • 4.1 この章について
    • 4.2 ノイズフィルター
    • 4.3 エッジ強調
    • 4.4 トーンカーブ補正
  • 第 5 章 応用編

    • 5.1 線形補間デモザイクの周波数特性
    • 5.2 高度なデモザイク処理
    • 5.3 ミラーレスカメラ(ソニーα 7-III) の RAW 現像
  • 付録 A

    • A.1 Colaboratory の簡単な使い方
    • A.2 RAW 画像の撮影方法
  • あとがき

付録は書籍版ではURLからのダウンロード方式になります。

内容プレビュー

以下、書籍からの抜粋です。購入検討のご参考にどうぞ。

f:id:uzusayuu:20190331124640p:plain:w150 f:id:uzusayuu:20190331124956p:plain:w150 f:id:uzusayuu:20190331132726p:plain:w150 f:id:uzusayuu:20190331132442p:plain:w150

ダウンロード頒布

イベントの雰囲気は何物にも代えがたいものですが一日限定イベントという事で当日来場できない方も多いかと思います。 私自身、海外在住の身のため過去五回の技術書展は参加できずはがゆい思いをしました。

そこで当日会場にいらっしゃることのできない方のために後日以下のURLでダウンロード頒布を開始する予定です。

https://moiz.booth.pm

販売開始はイベント当日夜以降の予定です。

追記

現在はBOOTHにて入手可能です。書籍+PDF版は2200円プラス送料、PDF版は1200円です。

moiz.booth.pm

最後に

委託頒布を快く引き受けてくださった@uchan_nosさんに改めて感謝します。

「低レイヤを知りたい人のための Cコンパイラ作成入門」を読んで、オレオレコンパイラを作り始めた

はじめに

表題通りですが、Rui Ueyama氏の「低レイヤを知りたい人のための Cコンパイラ作成入門」というPDF本を読んでCコンパイラ的なものを作り始めましたよ、というダラっとした内容です。

発端

ツイッター等で知ったのですが、セキュリティキャンプというイベントで、Cコンパイラを書く、というコースがあったそうです。

www.ipa.go.jp

内容については当時各所で話題になりましたし、参加者のブログなども多数書かれているようです。 たとえばこちらなど、コースの内容や雰囲気がよくわかります。

0x19f.hatenablog.com

hsjoihs.hatenablog.com

こういったものを見て、コンパイラ作成に興味を持ち始めたところ、そのセキュリティキャンプ講師のRui Ueyama氏が「低レイヤを知りたい人のための Cコンパイラ作成入門」を書かれた事を知りました。

booth.pm

PDF本は有料ですが、同内容のものがウェブページでも無料公開されています1

低レイヤを知りたい人のための Cコンパイラ作成入門

これはやるしかないだろう、という事で自分もオレオレコンパイラを書き始めました。

なお、目標はCに近い言語のコンパイラを作って見ることでコンパイラがどのようにできているか学ぶ、ということなので、Cの規格に正しく従っていない部分が多々あります。

とりあえず書いてみる

自分はコンパイラについての知識はまったくなかったのですが、PDF本の内容が懇切丁寧なので、その内容を実行するにあたってはあまり問題なくすすめる事ができました。

内容的にはまず、数字を一つ読み込んで出力するだけという、考えられる限りでもっとも単純なプログラムをアセンブラコンパイルできるだけの「コンパイラ」を作成します。次にそこに、四則演算を導入して、変数を導入し、と進んでいきます。

執筆途中とはいえ、現在公開されている記事の最後まですすむと、こんな内容のコードがコンパイルできるようになります。

a = 4;
b = 5 * 2;
a + b;

これをコンパイルして、アセンブラにかけると、示されたとおりの計算を行って、14という返り値を返す実行ファイルを生成することができます。

本の内容が丁寧なので、最初にまったく何もなかったところからここまで、意外なほと短時間でたどりつけました。

別のコンパイラ入門書をよんでみる

こうやってコンパイラを書きすすめてきたわけですが、残念ながらPDF本の内容はまだここまでしかできていません。

そこで、まず他の本を一冊読んでみることにしました。

日本語のコンパイラ作成入門の電子書籍を探していたところ、こちらの本に出くわしました。

明快入門 コンパイラ・インタプリタ開発 (林晴比古実用マスターシリーズ)

明快入門 コンパイラ・インタプリタ開発 (林晴比古実用マスターシリーズ)

この本の内容は、スタックマシンをエミュレートしたVM上で動作するCサブセットのコンパイラソースコードの解説と、その関連技術の説明です。

とりあえずこちらの内容を一通り読んで、コードを手入力して実行させてみました2

スタックマシンのエミュレータ、という点が独特ですが、最初に読んだ「低レイヤ〜」でもスタックマシン的なコードを出力していたので、わりとすんなりなじめました。

次にどうする?

ここまで来たところで元の自作コンパイラにもどったのですが、次に何をするのか途方にくれてしまいました。

そこで、セキュリティキャンプに参加した方から、Rui氏のブログでセキュリティキャンプではどのような順に実装を進めたのか書いてあると教えていただきました。

note.mu

とりあえず、こちらの方針に従って以下のように進めていくことにしました。

  1. 引数なしの関数呼び出し
  2. 引数ありの関数呼び出し
  3. 引数なしの関数定義
  4. 引数ありの関数定義
  5. グローバル変数
  6. 配列
  7. ポインタ
  8. char型
  9. 文字列リテラル
  10. 構造体
  11. #include

書くぞ、書くぞ、書くぞ

その後はひたすら実装テスト実装テスト実装テストの繰り返しです。

毎回機能を追加する度にテストを追加し、ちょっとずつちょっとずつできることが増えて、とうとう文字列リテラルのところまですすみました。

また、セキュリティキャンプの参加者の方から(ときには講師の方から)いろいろと実践的なアドバイスをいただけたのは非常に助かりました。 特に、X86-64のABIではスタックポインタは16バイトアラインされている必用がある、というのと、rbxなどはcallee save、というのは自分ではきっと絶対に気が付かないではまっていたと思います。

そんなみなさんの協力もあって、今ではこんなコードをコンパイルすることができるようになりました。

void print(int a) {
  int b, c;
  b = 1;
  while(a >= 10 * b) {
    b = b * 10;
  }
  while(b > 0) {
    putchar((c = a/ b) + 48);
    a = a - c * b;
    b = b / 10;
  }
}

void print_string(char *a) {
  while(*a) {
    putchar(*a);
    a = a + 1;
  }
}

int fib(int a) {
  if (a <= 1) {
    return a;
  }
  return fib(a-1) + fib(a-2);
}

int main() {
  int i;
  print_string("Fibonacci seriese: ");
  for(i = 0; i < 20; i = i + 1) {
    print(fib(i));
    putchar(32);
  }
  putchar(10);
  return 0;
}

このコードをコンパイルすると以下のような結果が得られます。(m99ccというのが今回作ったコンパイラ、fibonacciが上記のプログラム。)

$ ./m99cc program/fibonacci.c > tmp.s && gcc -o tmp tmp.s && ./tmp
Fibonacci seriese: 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181

まだまだ不完全ではありますが、ゼロから初めて2ヶ月にしてはそれなりに形になっているのではないでしょうか。改めてご協力いただいた方々に感謝します。

(まだ終わってませんが。)

リポジトリ

これまでの結果はこちらのGithubリポジトリーにアップロードしてあります。

github.com

便利な資料

コンパイラ作成中に、ここまでで紹介した以外にもいろいろ便利なリソースについて教えていただいたので、ここにまとめておきます。

セキュリティキャンプの参加者のリポジトリ

github.com

github.com

こちらのShinya Kato氏(id:mizu0x19f)とはすじょい氏(id:hsjoihs)のお二人からは別途何度も有益なアドバイスをいただきました。

オンラインコンパイラGodbolt

godbolt.org

ブラウザ上でCやその他コードをコンパイルしてアセンブラの結果が見られる便利なサイトです。

変わった名前だなと思ったら本名のようです。まさに神ツール。

セキュリティキャンプ講師のスライド

docs.google.com

セキュリティキャンプの講師をされたhikalium( id:hikalium )氏のスライドです。

hikalium氏からは他にもいろいろ教えていただきました。上記のGodboltもhikalium氏経由の情報です。

C言語規格のドラフト

http://www.iso-9899.info/n1570.html

書籍 Compilers: Principles, Techniques, and Tools 2nd By Alfred V. Aho

入門書より範囲の広い本を、と思って買ってみましたがまだ読んでいません。

Intel® 64 and IA-32 Architectures Software Developer Manuals

Intel® 64 and IA-32 Architectures Software Developer Manuals | Intel® Software

インテルCPUのマニュアルです。 読まなきゃいけないのはわかるのですが、私は個人的な事情でこの書類を見ると鬱に...。


  1. 自分は応援も兼ねてPDFの方を購入しましたが、実際に参照したのはウェブページの方です。なおまだ執筆途中ということで、掲載されている内容はまだ途中までだそうです。

  2. 実はソースコードがダウンロードできるので、こんな事をする必要はなかったのですが、ダウンロード用URLが奥付に書いてあるのに気が付かず手で入力してしまいました。