四畳半テクノポリス

コロナのストレスで気が狂い、D進した院生

BINファイル-HEXファイル 変換ツール

mbedコンパイラのbinファイルをHexファイルに変換します

下のボタンを押しbinファイルを読み込むことでIntelHEXに変換され出力される。


 ダウンロード  

 LPC1114は安価で高性能で入門者にとても良いマイコンであり、mbedコンパイラを使って開発を行うことが出来る。

 mbedインターフェースを用いずFlashMagicを使って書き込みを行おうとした場合、どうしてもbinファイルをHEXファイルに変換する必要が発生する。このような場合HEXツールやBIN2HEX等のツールが用いられるが、前者は使い方がややこしく、後者は64ビット環境で動作位しない。このような環境依存の問題がややこしく感じたため、このツールを開発した。

なんか途中でファイル名の設定とかめんどくさくなったのでその辺適当

院試

自分の心の整理も兼ねて大学の院試の現状について書こうと思う、私が今年受験する大学は次の3つである。

 正直言って金沢大学はかなり厳しい気がする。現状の僕の学力では、専門はかろうじて解けるレベルだし、数学はかなり怪しいレベルである。金沢大学の先生が僕のことをかなり気に入ってくれているようなので中々複雑な心境である。

 NAISTに関して言えば院の学生とコネクションが出来たので、小論文を添削していただける事になりかなり有利に進められそうである。TOEICも585点でもそこまで悪い方では無いらしいし、小論文さえ良ければ大丈夫らしい、体感で「6割は行けるだろう」という感じがする。特に立地がのどかな上大阪日本橋にかなり近いということで非常に気に入っている。凄く行きたい。

 JAISTもとても良い大学であり高専時代はこの大学院へ専攻科から編入で進学しようと考えていたので受験することにした。興味のある研究分野もあるので受けることにした。行って後悔することは無いと思う。

 以上が僕の受ける大学である。できることであれば金沢大学へ進学したいところだが中々厳しそうである。現在の研究室の卒業研究を中途半端に進めて閉まっている現状なので、先生に土下座して院試の勉強とゼミだけさせてもらえるようにしたほうがいいかもしれない。

 とりあえずマクシミン戦略でNAISTJAISTを攻めて金沢へ広げていく感じで行こうと思う。

 万が一全部落ちてしまったら、卒業後研究生にでもなって、受験後に海外インターンシップでも行こうと考えている。というか考えてでもいないと心が潰れそうである。

 

 

「クジラの文化 竜の文明」大沢 昇

 以前神保町の古本市で三冊500円のまとめ売りで買った本である。

本のタイトルのクジラは日本のことであり、竜は中国のことである。

内容としては日米の文化の対比を歴史、文化、地理的要因、宗教、フォークロアなどの様々な側面から考察している本である。また中国と日本の類似性を語る上でアジア圏の文化と欧米文化との比較も登場する。

f:id:toriten1024:20170502173207j:plain

概要

一章 「顔」と「国の形」

 主に地理や歴史的な側面に関しての比較を行っている。国の成立の経緯や皇帝と天皇の違いなどについて詳しく解説している。最初は「日本鬼子」や「艦隊これくしょん」といったオタクカルチャーや日本から中国に流入した漢字の話から入り、それを起点に日本と中国の文化の根本的差異や類似点に関して話を広げていく。

 特に興味を引かれたのは皇帝に関する話である。私たちの日本の天皇は伝説上では神の血を引いていることになっており、血筋によって決められるものであるが、中国の皇帝は能力のある人間が選ばれるシステムであり、どのような身分の人間であっても運さえ良ければ皇帝になれたということである。そのせいか、天皇は位を子供に譲ることで退位するのがもっとも多いパターンであったようだが、皇帝が退位する要因としては一番が寿命や病死などによる退位、二番目が廃位、三番目が殺害であったという。

二章「水の文化」と「火の文明」

 機構的要因や生活様式から生じる、衣食住の文化に関する対比を行っている。タイトルの「水」と「火」という部分についててであるが、これはそれぞれの国においてのの浄化の象徴である。

 巨大な大陸にあり、大河が流れている中国における浄化の衝動は食べ物や水を消毒できる「火」であり、それが調理法などにも大きな影響を与えている。それに対し、細長い島国であり、中央の山脈から左右に短い川が流れている。かつて日本を訪れたオランダ人土木技術者も日本の川を見て「これは川でなく滝だ」といったという。このような短く水源から海までの距離の近い日本の川は淀むことがなく、常にきれいな水が手に入ったこのようなことから日本で「水に流す」という言葉があるように、日本における浄化の象徴は水であるという。

三章 どちらも現実主義だが

 国民性というか、国民の気質やものの考え方に関する対比を行っている。この章で私が面白いと感じたのは3節の”「縮みの志向」と「巨大願望」”である。簡単に言えば日本人は何でも小さく纏めてしまうことを好み、中国人は大きく巨大で壮大なものを好むというはなしである。

 このような傾向は建造物で顕著に現れており、中国の都市部へ行けば巨大な建物が沢山あるという、以前私は親戚に会うためにシンガポールを訪れた時に、多くの巨大なビルを目にし、度肝を抜かれた経験がある。日本にも都庁をはじめとする単純に大きい建造物は多数存在するのだが、それらとシンガポールの建物は空間のつくりがまったく異なっているのだ。巨大な吹き抜けやマリーナベイサンズのような一見無茶な建造物を多数みかけた、このような傾向はやはりシンガポールの経済の中枢を華僑が回していることが影響しているのだろう。

 また、本に登場するエピソードで、大学生として興味を惹かれるものに、こんな話があった。中国からきた留学生が日本の大学で研究テーマを決めるとき大きく壮大なテーマを選ぼうとする。すると日本人の指導教官からダメだしをもらい、もっと小さなテーマを選ぶよう促される。その後中国に帰りかえり研究テーマを尋ねられて答えると、「わざわざ留学までして、そんな小さなテーマを扱ったのか」と文句を言われるのだという。このような一見文化と関係なさそうなアカデミックな領域まで国民性が関与してくるのは意外であった。

四章 明るい競争社会の裏側

 移動手段や文学や芸術に関しての対比が行われている。タイトルの意味は良く分からなかった。

 移動手段に関しては国土の性質が強く影響しており、中国の辞書では日本で言う「衣食住」の項目が「衣食住行」となっているのだと云う。比較し狭い面積でかつ細長い日本では東海道などに見られるよう徒歩による文化が発達し、縦横それぞれにとても広い国土をもつ中国では乗り物による移動の文化が発達したのだという。

 文学に関しては宗教の影響が強く影響している。古くより日本では仏教神道が信仰されており、幽霊が創作の題材に取り扱われる。これには日本の神道が単なる体系性をもった多神教ではなく、地域の特性を色濃く持つ土着宗教に近い性質を持っていたり、仏教に関してもかなりのローカライズがなされている、ことが影響しているのかもしれない、それに対し「儒教思想の支配が強い中国では孔子が人知の及ばないところに関しては語るべきでない」といっているように幽霊などは民の時代までは、あまり文学の題材として扱われることはなかったという。中国は欧米や日本と比べ創世に関するハッキリした伝承がないためフォークロアに関しても自由な発想のものが多く、孔子以前の古い民話や伝説が失われてしまっていると思うと少し残念である。

五章 「クジラの文化」と「竜の文明」

 タイトル回収の章である。日本がクジラ、中国が竜に例えた理由やそれに対する今後の展望にかんして書かれている。

 竜は欧米の伝説ではお姫様が竜にさらわれたり、黙示録の獣が竜であるように悪の化身として扱われるが、中国では神聖なものとして崇められ、中国人は自分たちは竜の血を引くと自称するという。筆者は中国の竜の特徴は様々な生き物の特徴の複合であることを挙げている。竜の頭は麒麟にており、鹿に似た角と、鯉に似た髭を持つなどのキメラ的特長を持つ、このことから他民族が入り混じり多数の文化が組み合わさった中国の文化との類似性なども含め中国を竜の文明としている。

 それに対し、日本の象徴としてクジラを挙げている、日本の文化として紀元前よりクジラを食べる文化を持ち、鯨の肉から、髭、内臓に至るまで余すことなく使い、クジラを弔う「青海島鯨墓」があるなど、鯨が日本文化に密着していること、中国と近すぎず離れすぎず独自に進化したことなどを挙げ日本をクジラの文化としてる。

感想

 まず購入したときの値段に対してかなり楽しめる本だったので満足していることを書いておきたい、中古なので筆者にお金がまわらない事を少し申し訳なく思う。

 私は以前よりエンジニアとして東洋のシリコンバレーと呼ばれる「シンセン」対し強い憧れを持っていたので中国に興味はあったのだが、技術的な側面でしか見ていなかった中国に対し様々な側面で見識を深めることができた。

 エンジニアとして興味を惹かれたものに「落」という概念があった。中国には「盗」と「落」とう二つの概念があり、「盗」は盗みで罪であるが、自分の仕事場のものを少し持ち帰って使うことは「落」にあたり、罪ではないということである。聞く話によるとシンセンのジャンク屋に行くとiPhoneのCPUなどが平然と売られているという、iPhoneの製造は中国のFoxConの工場で行われているというが、こういった部品が流出している事態も「落」に当たるのかと考えてしまった。

大学のサークルで技術書典2に出展してきました。

技術書典

 コミックマーケットでは以前より技術島と呼ばれる一角があって、技術関連の同人誌が並べらており、メイカーズムーブメント以降その数は増えているものと思われる。しかし、世界最大級の同人誌即売会であるコミックマーケット、技術書の占める割合は極一部に過ぎず、技術書を購入しようと思って行っても、エロ本の中をかき分け無いと、だどりつけない状況にある。

 4月9日に開かれた、技術書典はコミックマーケットとは異なり。技術書が主役、いや、技術書と同人ハード/ソフトのみの同人誌即売会である。よって、お子様連れや中学生でも安心して同人技術書を購入する事ができる。

大学のサークルで出展

 私が所属するTUT-CC(豊橋技術科学大学コンピュータクラブ)は以前よりコミックマーケットに参加しており、毎回各部員が自主的に学んできたことに対して、思い思いの記事を書いてまとめた会報的な部誌を頒布している。実を言うと私がこのサークルに入ることにしたのは、豊橋技科大に合格した年の冬のコミックマーケットTUT-CCが出展しているのを見つけた事が決め手となっており、今回部誌に投稿出来たことは非常に光栄に感じている。

 今回は新任の部長さんが取りまとめをやってくださったのだが、私はひどい乱文を校閲させてしまったり、gitの部誌のリポジトリで自分の記事をセルフマージしてしまったり、モノクロ画像しか使えないのに図が白黒だったので気づかずにカラー画像を挿入して印刷を遅らせてしまったり、散々迷惑をかけてしまって大変申し訳無かった。(本当にありがとうございました。)そんなこんなでなんとか印刷は間に合い技術書典に出展することは出来た。

 ちなみに私は以前掲載したステレオマッチングに関する記事を詳細に解説したものを投稿した。図もかなり増やしてわかりやすく説明したつもりだ。

 開場直後は弊サークルの同人誌を買っていく人は少なかったものの、技術書専門の同人誌即売会ということで結局身内が集まり、買っていった人の半分くらいは、部長さんの知り合いと、私の高専時代の後輩(都立高専なんで会場の近所に住んでいる)、それから豊橋技科大のOBだったと思う。残りは立ち読みして興味を持ってくれた他大学の学生や社会人で、私の記事に興味を持って買って行ってくれた人もいて、とてもうれしく感じた。

 結果としては既刊と新刊ともにすべて売り切ってしまった。コミックマーケットでは同じ分量を吸って半分売れ残っていたようなので、技術書典恐るべしである。

戦利品

 同人誌即売会ということでもちろん他のサークルの頒布している同人誌も購入してきた。あまりお金を持って行かなかったのと、買い物に行ける状態になったのが客足が減ってからだったので多くの本を購入することは出来なかった。買えたのはこの四冊である。

f:id:toriten1024:20170412024250j:plain

 立ち読みしてお金の許す範囲で購入したので基本どれも面白いものを購入したつもりだが、おまんじゅう本舗刊のSTM32マイコン入門は入門書として非常に良く出来ており、スイッチサイエンスの同人誌コーナーで販売してもいいんじゃないかというクオリティである。

 

今回初めて同人誌即売会に出展側として参加したが、お客さんとして参加するのとは違い緊張感を持った中々面白い体験だった。いつかは自分で同人誌を書いて参加してみたとい思う。

BootCampが「no bootable device」になったときにwindowsの復旧出来た話

パーティション編集に失敗

 先日macbootにubuntuをインストールしようと考え、OSXの下方にubuntu用の領域を作成したところ、bootcampでインストールされていたwindows7を起動しても「no bootable device -- insert boot disk and press any key」としか表示されなくなってしまった。おそらくパーティション編集時にマスタブートレコードを傷つけてしまい、ブートローダのアドレスなどがおかしくなってしまったものと考えられる。

optionキーを押しながら起動すればmacに入ることはできるのでそこから再インストールすることも可能であったが、windows7にはフルパッケージのCygwinFPGAの開発環境のようなインストールのややこしいソフトがいくつかインストールされており、できることなら再インストールして環境を再構築することは避けたいと考えた。そこでwindous7のリカバリーメディアによるマスターブートレコードの修復を試みた。

よってこの記事はwidowsの領域を破壊していないが、マスターブートレコードや ブートマネージャに問題が発生し、mac上のwindowsが起動できなくなってしまった人の役に立つものだと思う。

リカバリメディアの作成

要はwindows7のインストールメディアを用意すればいいのである。OSXを起動してインストールメディアを作成する。インストールメディアのisoファイルはbootcampでインストールした人間であれば、おそらくダウンロードしたものがOSX上に残っていると思うが、残っていない場合はプロダクトキーを使って、次のサイトからダウンロードすることができる。

https://www.microsoft.com/ja-jp/software-download/windows7

このisoファイルをbootcampアシスタントではなくmacのディスクユーティリティより「ディスクの作成」を使って、DVDに書き込む。

windowsの復旧

ディスクを入れ起動時に「Option」を押すとbootメニューの中に円盤のアイコンが現れると思う、これがwindowsのインストールメディアであるので、選択し、起動する。

言語選択の画面が表示されるので日本語を選択し、次に進むと今すぐインストールの左下に「コンピュータを修復する」という項目があるはずである。

それをクリックすると「システム回復オプション」というウィンドウが表示されるので、上下2つのチェックボックスのうち上の方を選択し、次に進む。

するといくつか修復の項目が表示されると思うので、「スタートアップ修復」を選択すると修復が開始する。

と、まぁ 普通のスタートアップ修復である。

 

OSというのはどうしても大量のソフトウェアをインストールするうちの構築に数日かかるような環境が構築されてしまいがちなので、macを使っているのであれはwindwsのパーティションも含め普段からTimeMachineでバックアップを取っていいたほうが良いであろう。

CUDAによるジュリア集合の描画

 定期試験も来週に迫っているわけであるが、凝りもせず、こんな駄文をインターネットに垂れ流している。というのも、今週にもあった定期試験を対した努力もせず、パス出来たからである。人間というのは、楽な課題だけ選んで挑戦していれば、努力せずともそこそこの成果を得られてしまう、それだけならいいのであるが、結果を得ることで自分が優秀な人間であると勘違いしてしまうことがある、これは恐ろしいことである。知らず、知らずのうちに能力がどんどん下がっていってしまうからである。

 僕は以前も書いたように豊橋技科大のコンピュータクラブに所属しプログラムを書いているわけであるが、ゲームのような一般人から見てもわかりやすいものを作っているわけでも無く、文化祭の際にこれといって展示できるようなものが無かったた。以前からCUDAとOpenCVによる画像処理を行っていたため、CUDAのカーネルを記述することが出来た。そこでCUDAを用いてマンデルブロ集合やジュリア集合と言ったフラクタルを描画するプログラムを作成した。

1.フラクタル

 こんなブログを訪ねてくる人であれば大抵フラクタルについて理解はしているであろうが、一応簡単に説明しておく、僕はその分野の数学を専攻した事が無いので詳しい概念を理解しては居ないのだが、簡単に言うと無限の階層を持つ自己相似形の集まりである。

 分からない人は、そんなことを言っても分かりづらいと思うので、身近な例を挙げるとブロッコリーやカリフラワーの構造であろう。サラダに入っている小さく切られ、茹でられたブロッコリーと、スーパーで売られているブロッコリーは相似形である。更に、サラダに入っている茹でられたブロッコリーの一部を手でちぎると、そのかけらはサラダに入っているブロッコリーの切れ端とも、スーパーで売られているブロッコリーとも相似形である。簡単にいうと一部と全体の形が同じなのである。

 ブロッコリーはちぎっていくとつぼみ一つ一つになってしまうため、階層は有限であるが、数式で記述されるフラクタル図形は計算時間とアウトプットの解像度が許す限り無限の階層を持つことができる。

 

f:id:toriten1024:20170218041849p:plain

2.ジュリア集合とマンデルブロ集合

 僕は実際この集合の数学的な意味に関しては理解出来ていない、ただ映しだされる模様が美しいので好きなのである。次の画像がジュリア集合を描画した一例である。これは僕が作成したプログラムで描画したものである。

f:id:toriten1024:20170218042804j:plain

ジュリア集合は次の式で表される。

z_{k+1} = Z_{k^n+C}

 この計算を繰り返していくと、Zの値が変化し、発散する場所が出てくる。それに必要な計算回数をプロットしていくと以上の様な幾何学模様が現れるのである。zというのは複素数zでありz = z+yiという定義からX座標Y座標さえあれば計算することができる。

 このへんは数式で考えると頭が痛くなってくるので後々プログラムで説明するが、この計算の初期パラメータCを変化させることで、ジュリア集合は様々な形に変形し、美しい幾何学模様を描くのである。

3.実装

 今回作成したプログラムは前節せ説明したパラメータCを変化させジュリア集合の形が変化していくのを描画するだけのものである。様々に変化するジュリア集合の美しい模様を観察することができる。

 CUDAのカーネルは通常のC++と別に.cuファイルを作る必要があるため実装は2つのファイルに分けて行った。一つ目がOpenGLで描画を行うC++ファイル、2つ目がGPGPU計算を行うCUファイルである。

man.cu

#include "man.cuh"
#include "config.h"
#include <cuda_runtime.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <device_launch_parameters.h>
#define BLOCK 32

__global__ void cal_pix(float* inMatA,float ci);

void draw_gpu( float *ptr_h){
	int matrixSize = sizeof( float) * V_MAX * (H_MAX*3) ;
	float* hMatA;
	hMatA = (float*)malloc(matrixSize);
	int col, row;
//初期化 for(col = 0; col < V_MAX; col++){ for(row = 0; row < H_MAX; row++){ hMatA[(col * H_MAX + row)*3 + 0] = 0; hMatA[(col * H_MAX + row)*3 + 1] = 0; hMatA[(col * H_MAX + row)*3 + 2] = 0; } } float* dMatA; cudaMalloc((void**)&dMatA,matrixSize); cudaMemcpy(dMatA,hMatA,matrixSize,cudaMemcpyHostToDevice); dim3 block(BLOCK, BLOCK); dim3 grid(V_MAX / BLOCK,H_MAX / BLOCK); static double cnt = 0.0; cnt = cnt +0.01; double cir = sin(cnt) * 0.5; //カーネルの呼び出し
cal_pix<<< grid, block >>>(dMatA,cir); cudaThreadSynchronize(); cudaMemcpy(ptr_h,dMatA,matrixSize,cudaMemcpyDeviceToHost); free(hMatA); cudaFree(hMatA); cudaThreadExit(); } __global__ void cal_pix(float* inMatA,float ci){ int col = blockIdx.x * blockDim.x + threadIdx.x; int row = blockIdx.y * blockDim.y + threadIdx.y; float x,y,xx,yy,zr,zi; double l; x = (3.0)*((float)col/V_MAX) - 1.5; y = (3.0)*((float)row/H_MAX) - 1.5; xx = x; yy = y; for(l = 0; l < 16; l+=0.05){ zr = xx*xx - yy*yy +ci; zi = 2*xx*yy+0.64; xx = zr; yy = zi; if((zr*zr + zi*zi) > 4) break; } inMatA[(col * H_MAX + row)*3 + 0] = (l > 15.5)? 0: l/16; inMatA[(col * H_MAX + row)*3 + 1] = (l > 15.5 || l < 1.0)? 0: (16 - l )/16; inMatA[(col * H_MAX + row)*3 + 2] = ( (int)(l*20) & 1)? l/16: (16 - l )/16 ; }

 CUDAカーネルはc++ファイルから呼び出す事が出来ないので間にdraw_gpu関数を挟む、この関数はGPU上に渡す画像の配列データとの初期化を行い、cal_pix関数へ渡す。この関数は渡された情報を基に自分の担当するピクセルがどのような条件で発散するかを調べ配列データに格納する。

gpu.cpp

#include <GL/glut.h>
#include "config.h"
#include "man.cuh"
#include <math.h>
#include <stdio.h>
float vram[(V_MAX * H_MAX)*3];

void idle(void)
{
  glutPostRedisplay();
}
void display(void)
{
  static float cnt = 0;
  cnt = cnt + 0.01;
  int i=0,j=0;
  glClear(GL_COLOR_BUFFER_BIT);
  glBegin(GL_POINTS);
  int pos;
  draw_gpu(vram); 
  for(i = 0; i < V_MAX ; i++){
	for(j = 0;j < H_MAX; j++){
	  pos = ((V_MAX * i) + j) * 3;
	  glColor3d(vram[pos+0] , vram[pos+1], vram[pos+2]); /* 赤 */
          glVertex2d(4.0*((float)i/V_MAX) - 2.0 ,4.0*((float)j/H_MAX) - 2.0);
	}
  }
	  
  glEnd();
  glutSwapBuffers();
}

void resize(int w, int h)
{
  glViewport(0, 0, w, h);
  glLoadIdentity();

  glOrtho(-w / (V_MAX/2), w / (V_MAX/2), -h / (H_MAX/2), h / (H_MAX/2), -1.0, 1.0);
}

void init(void)
{
  
}

int main(int argc, char *argv[])
{
  glutInitWindowSize(V_MAX, H_MAX);
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
  glutCreateWindow(argv[0]);
  glutDisplayFunc(display);
  glutReshapeFunc(resize);
  init();
  glutIdleFunc(idle);
  glutMainLoop();
  return 0;
}

 main.cppファイルはcudaを呼び出し描画するだけの簡単な処理を行う関数である。描画はOpenGLによって行っており、表示にはGULTを使用している。

 コンパイルは次のMakefileで行った

 

gpud: man.o gpu.o
	nvcc  -O2 -o gpu gpu.o man.o -lglut -lGLU -lGL -lm
gpu.o: gpu.cpp man.cuh config.h
	nvcc -arch=sm_20 -c  gpu.cpp -lglut -lGLU -lGL -lm
man.o: man.cu man.cuh config.h
	nvcc -arch=sm_20 -c man.cu
clean:
	rm man.o gpu.o

 

 CUDAを使って計算してみて驚いたことは、CUDAで計算してCPUに書き戻すまでのオーバーヘッドが思ったよりも大きかったことである。結果として発散までの計算回数が少ない場所ではCPUでジュリア集合の計算を行った場合に比べ、CPUで計算したほうが高速な部分が見受けられた。これには少し落胆してしまった。(とは言っても僕が使用しているGPUがGTX720というintelHDに毛が生えたレベルなのが原因かもしれないが)

 

pythonによるpng画像の32bit透過付きBMPへの変換

 png(Portable Network Graphics)はアルファチャネルによる透過機能持ち、圧縮効率と色の表現性のバランスのとれた便利な拡張子であり、Web開発や,ゲームで使用される画像やテクスチャファイル,として良く用いられる。しかし、pngのファイルの圧縮原理を理解し、自力で再生することはそれほど容易なことではない。

 pngをメモリ上にビットマップ(拡張子のBMPではない)として展開して使用するには、「libpng」というオープンソースのライブラリが存在するが、これにはzlib Licenceというライセンスの影響を受けてしまい、ソースコード内にライセンス通知文章を含まなければならなくなる。私自身の個人的な主義だが、正直に言ってライセンスと言った様な契約が好きでなく、制作物はなるべくライセンスフリーで配布したいと考えている。

 zlib Licenceは非常に緩いライセンスであるが、それでも自分の制作物に他者が宣言したライセンスが混入するのが嫌であったので、「BMP形式のファイルでなんとか透過画像を作る事が出来ないか」思案していたところ、BITMAPV4という規格ではビットマップにアルファチャネルを追加し透過することができることを知った。(ただし一般的な規格ではないので環境によっては表示出来なかったり崩れたりすることがある。)

 早速png形式の画像を32bitのBMPに変換するソフトウェアを探してみたのであるが、Linuxに対応しているものは少なく、日本語情報も余り見つからなかったため、以前よりBMPのフォーマットに興味があったこともあり、勉強も兼ねて、pythonpng画像を透過付きbmp画像に変換してみることにした。

1.アルファチャネル

 透過付き画像を語る上では、まず、画像のアルファチャネルについて解説せねばならない、アルファチャネルとは、ビットマップイメージに対する透過度を示したマップであり、各ピクセルごとに、一つづつ透明度の情報が付加される。 画像の透明度はアルファチャネルをRGBそれぞれに乗算することで求められる。アルファチャネルは0〜1の間で変化すると定義されているが実際には8ビットint型であり、0〜255のレンジの百分率となる。例えばRGBそれぞれ8bitカラーの画像に対し、アルファチャネルを付加するとすると、1ピクセルあたり32bitの情報量になる。

 アルファチャネルを持った画像同士の合成には、色々な種類があるが、今回は加増の重ね合わせ、一般的に言われるアルファブレンドだけついて解説する。まず、上に重ねる画像をX、下側の被せられる方の画像をYとし、重ね合わせによって出力されう画像をOutとし、それぞれのR,G,B,のそれぞれのチャネルをX_{RGB}と呼び、アルファチャネルをX_{A}と呼ぶことにする。

 まず重ね合わせ後のアルファチャネルの計算は次のようになる。

Out_{A } = X_{A} + Y_{A}(1 - X_{A})

この時計算結果が1を超えてしまった場合は1にする。計算結果を用いてR,G,Bそれぞれのチャネルをブレンドしてゆく、すると重ねあわせた後の画像は次のようになる。

Out_{RGB} = \frac{ ( X_{RGB}\, X_{A} + Y_{RGB} \,Y_{A}(1 - X_{A}) )} {Out_{A}}

2.python上でのpngの扱い

 pythonでは、一般的に普及しているほとんどのラスタ画像を読み書きできるPILというライブラリが存在する。このライブラリは画像の読み込みや、表示だけを目的に作られたものでは無く、画像処理に関する様々な機能を持っているが、今回はPILを用いてpng画像のR(red),G(green)B(blue),A(Alpha)のそれぞれのチャネルにアクセスする。

 PILはデフォルトでは入って居ない場合があるのでpipを用いてインストールする。今回はここのサイトを参考に次の様にコマンドを実行してインストールした。

$ sudo pip install pillow

 次に実際にPILが動作するか確かめるために、pythonの対話モード次の様なプログラムを実行しPILの動作確認を行った。

from PIL import Image
image = Image.open("任意のファイル名")
image.show()

 これで画像が表示されれば、正しくPILがインストールされたことが分かる。

3.透過付きBMPのヘッダの構造

 透過つきBMPのファイル構造はMicrosoftが拡張したものであり、通常の32bitのBMPファイルとは異なった情報を持っている。これらのフォーマットの各要素はリトルエンディアン(最下位バイトから順に)で格納されるので注意せねばならない。

BMPのファイルフォーマット

アドレス名前サイズ[Byte]
0x00 bfType 2 "BM"の二文字を格納、BMPであると宣言
0x02 bfSize 4 ファイルサイズ(width + height)\times4+header
0x06 bfReserved2 2 予約で0を格納
0x08 bfReserved2 2 予約で0を格納
0x0A bfOffset 4 画像データまでのオフセットを格納(今回は134)
0x0E biSize 2 以下に続くオプションの情報ヘッダのサイズ(今回は0x6C)
0x12 BiWidth 4 横方向の解像度
0x16 biHeight 4 縦方向の解像度
0x1A biPlane 2 プレーン数、常に1
0x1C biBitCount 2 画像のビット数(今回は32)
0x1E biComperession 4 RGB並びで非圧縮
0x22 biSizeImage 4 画像のサイズが格納されるが、不要なので0
0x26 biXPerMeter 4 横方向の一メートルあたりのドット数、今夏は0
0x2A biYPerMeter 4 縦方向の一メートルあたりのドット数、今回は0
0x2E biCirUse 4 色のパレット数、今回は0
0x32 biCirImportant 4 パレットの重要色数、今回は0
0x36 red_mask 4 32bit中の赤色の桁を示すマスク(0x000000FF)
0x3A green_mask 4 32bit中の赤色の桁を示すマスク(0x0000FF00)
0x3E blue_mask 4 32bit中の赤色の桁を示すマスク(0x00FF0000)
0x42 alpha_mask 4 32bit中の赤色の桁を示すマスク(0xFF000000)
0x46 CsType 4 色の空間を示す("BGRs"の四文字)
0x4A Endpoints 36={3*3*4} RGB-XYZ変換テーブル今回はすべて0
0x6E gamma_red 4 赤色空間のガンマ値
0x72 gammma_green 4 緑色空間のガンマ値
0x76 gamma_blue 4 青色空間のガンマ値
0x7A red 4 不明、おそらく赤色空間のマスク
0x7E green 4 不明、おそらく緑色空間のマスク
0x82 blue 4 不明、おそらく緑色空間のマスク

 ヘッダーに関する解説は以上の通りである。実際のファイルではこの後に続いてRGBAのデータが格納されてゆく、格納されるビットマップのデータはRGBAの並びで格納される、通常のビットマップではBGRであるので注意が必要である。

4.実装

 PILライブラリと透過付きBMPのヘッダフォーマットを元に、png画像を透過付きBMPに変換するプログラムを作成した。コードは次に示す通りである。

  1. 流れとしてはまずPILのImageでpng画像を読み込み、Image型の画像データをBMPで保存する関数へ渡す。

  2. 渡された関数は、引数から横幅やファイルサイズなどの情報を計算し、それを基にヘッダを作成する。

  3. ヘッダの情報にImage型の引数をRGBA形式に変換しヘッダに連結する。
  4. バイナリとしてファイルに書き込む

bytearrayにデータを追加していき、書き込むだけという単純なものである。基本的にPILで開くことのできるイメージファイルであれば、すべて対応していると考えられる。

 

from PIL import Image
import sys
#convert int value to 4byte bytearray
def int_2_4byte( val):
    return bytearray([val  & 0xff ,val >> 8 & 0xff, val >> 16 & 0xff ,val >> 24 & 0xff])

#convert int value to 2byte bytearray
def int_2_2byte( val):
    return bytearray([val  & 0xff ,val >> 8 & 0xff])

#save as 32bit transmission bitmap
def con2bmp( _src  ,FileName):
    rgba_im = _src.convert('RGBA')
    pix = im.size
    size = pix[0] * pix[1] * 4 + 134
 
    bary = bytearray()
    #Bitmap File Header
    """0000"""  
    bary.extend( bytearray(['B','M']))
    """0002"""
    bary.extend( int_2_4byte(size) )
    """0006"""
    bary.extend( bytearray([0,0]) )
    """0008"""
    bary.extend( bytearray([0,0]) )
    """000A"""
    bary.extend( int_2_4byte(134) )
   
    #Bitmap Information Header
    """000E"""
    bary.extend( int_2_4byte(0x6C)) #stable
    """0012""" 
    bary.extend( int_2_4byte(pix[0]) )
    """0016"""
    bary.extend( int_2_4byte(pix[1]) )
    """001A"""
    bary.extend(  int_2_2byte(1) ) 
    """001C"""
    bary.extend( int_2_2byte(32) ) #save as  32bit bit map
    """001E"""
    bary.extend( int_2_4byte(3) )
    """0022"""
    bary.extend( int_2_4byte(0) )#dummy
    """0026"""
    bary.extend( bytearray([0xFF,0,0,0]) )	
    """002A"""
    bary.extend( bytearray([0xFF,0,0,0]) )	
    """002E"""
    bary.extend( int_2_4byte(0) )
    """0032"""
    bary.extend( int_2_4byte(0) )
   
    """0036"""
    bary.extend( bytearray([0xFF,0x00,0x00,0x00]) )
    """003A""" 
    bary.extend( bytearray([0x00,0xFF,0x00,0x00]) )
    """003E"""
    bary.extend(  bytearray([0x00,0x00,0xFF,0x00]) )
    """0042"""
    bary.extend( bytearray([0x00,0x00,0x00,0xFF]) )
    """0046"""
    bary.extend(  bytearray(['B','G','R','s']) )
    """0x004A"""
    for i in range(36):
        bary.extend([0x00])
    """0x006E"""
    bary.extend( int_2_4byte(1) )
    """0x0072"""
    bary.extend( int_2_4byte(1) )
    """0x0076"""
    bary.extend( int_2_4byte(1) )
    """0x007A"""
    bary.extend( bytearray([0xFF,0x00,0x00,0x00]) )
    """0x007E""" 
    bary.extend( bytearray([0x00,0xFF,0x00,0x00]) )
    """0x0082"""
    bary.extend( bytearray([0x00,0x00,0xFF,0x00]) )

 
    #ping Imgae to Bit map  data
    for y in range(pix[1]):
        for x in range(pix[0]):
            r,g,b,a = rgba_im.getpixel((x, pix[1] - (y+1) ))
            bary.extend(bytearray([r,g,b,a]))
            
    #OpenFile
    with open(FileName, "wb") as fout:
        fout.write(bary)
args = sys.argv
im = Image.open(args[1])
con2bmp(im,args[2])

プログラムの実行は次の様に行う

python png2bmp.py [変換対象].png [変換後].bmp

 

 残念ながらブログ上では、画像がすべてjpgに変換されてしまうため、この画面に写っているb画像はどちらもjpgになってしまっているが、左側の画像がpng画像、右側の画像がBMP画像である。余り画質に変化は見受けられない、また、ファイルサイズが2150[%]も増加してしまっている事からpngBMPと比較しサイズ面で非常に優れている事が分かる。

 

png画像の透過付きビットマップへの変換

変換前(PNG変換後(BMP
f:id:toriten1024:20170213181605p:plain f:id:toriten1024:20170213181610j:plain
11,065 byte 237,878 byte

 

 ライセンスを避けるために透過付きBMPを利用しようと考えたが、実際に使える事が分かった反面、ファイルサイズにおいてはPNGの方がはるかに優れていることを肌で感じた。やはり本当にライセンスを回避したいのであれば、PNGのファイルフォーマットを学んだほうが良いだろう、残念ながら僕にはまだそのレベルのプログラムを記述する実力が無い。