JpegOrientationとRotateFlip

携帯で撮った写真を,tbasicで表示するとします。それには,その画像ファイルの大きさのグラフ画面をGScreenを開いて,LoadPictureコマンドで,そのファイルを表示させるだけです。これはtbasicでの基本的なグラフ処理です。

これはこれで良いのですが,この方法では,画像によっては,

(1)LoadPictureを使う

と横向きに表示されることがあります。ところが同じファイルを画像表示アプリ,例えば,フォトで開くと,

(2)フォトを使う

となります。こちらが期待するものでしょう。では,tbasicでもこのように表示するにはどうしたら良いのでしょうか。

それは,画像を回転させれば良いでしょう。

今までtbasicでは,画像回転はできませんでしたが,Ver.1.62で画像回転コマンド,RotateFlipをサポートしました。ここでは,この周辺の事柄について説明します。

今まで,tbasic内での画像回転はできませんでしたが,Windowsでのexplorerを使えば簡単にできるので,必要な場合,それで対応可能でした。しかし,検討の結果,RotateFlipは実現可能なので,今回サポートしました。

tbasicのRotateFlipはC#でサポートされているものと機能的にほぼ同じで,高速に動作します。指定方法は少し異なります。

RotateFlip(Angle)
RotateFlip(Reflection)

の形で指定します。RotateFlip(Angle)は,現在のグラフ画面をAngleだけ時計回りに回転させます。Angleの取れる値は,90度,180度,270度のみです。次のように文字列の形式で記述します。
RotateFlip(“90”),RotateFlip(“180”),RotateFlip(“270”)

RotateFlip(Reflection)現在のグラフ画面をReflectionで指定された方法で反転させます。反転はx軸,y軸に関する反転のみです。
RotateFlip(“x”)は画像をx軸に関して反転させます。
RotateFlip(“y”)は画像をy軸に関して反転させます。

このRotateFlipを使って,上の(1)の画像に対して,
RotateFlip(“90”)の処理をすれば(2)の画像になります。

tbasicで画像が横向きになっていた場合,上のように,RotateFlipで対応可能ですが,フォトを使った場合,元々横向きになりませんでした。この違いは何でしょうか。
フォトの場合,実は,上の処理を自動で行って回転処理後の画像を表示していたのです。そして,この自動処理を可能にするのが,Exifです。

Exifは”Exchangeable image file format for digital still cameras”のことで,デジタルカメラで撮影した際の,カメラの状態の情報を画像ファイルに埋め込むための仕様です。デジタルカメラで作成されたJpegファイルに格納されます。

カメラのメーカー,撮影日時,シャッター速度,絞り,等々多くの情報を記録することができます。

この中で,画像方向 Orientation があります。デジタルカメラで撮影・保存された画像は,カメラの位置により方向修正が必要な場合があります。このとき,その修正方法を記述するものです。

Orientationは1~8の整数値を表し,次の意味を持ちます。

 1 : 回転なし
 2 : 左右反転
 3 : 時計回りに180度回転
 4 : 時計回りに180度回転かつ左右反転
 5 : 時計回りに90度回転かつ左右反転
 6 : 時計回りに90度回転
 7 : 時計回りに270度回転かつ左右反転
 8 : 時計回りに270度回転

JpegファイルのOrientation値が1でない場合は,その値に応じて画像を回転等させれば,正しい位置に画像が表示されます。

実際,(1)で表示された画像は,Jpegファイルで,そのExifのOrientation値は6でした。

フォトが(2)の画像を表示可能なのは,画像ファイルのOrientation値を利用して,その値に応じた変換を施した後に,画像を表示しているからです。

Orientation値を知ることができれば,目で画像を見なくても位置修正が可能ですが,では,Orientation値はどのようにすれば知ることができるのでしょうか。いくつかの言語で実装する例があります。例えばC#では,bitmap.PropertyItemsを利用して,Orientation値を取得できます。直接値を示す関数がある言語もあります。現在,tbasicではそのような関数はありませんが,tbasicでもReadAllBytesを利用すれば,Orientation値を与えるユーザー定義関数を作ることができます。以下にそのプログラムを挙げますが,ここでは,その仕組みを簡単に説明します。
Exifの詳しい内容はその仕様書,例えば,CIPA DC-008-2026で知ることができます。

(1)Exifデータは,Jpegファイルに埋め込まれているので,Jpegファイルの内容(バイナリデータ)を読み取る必要があります。ReadAllBytesを利用すれば,バイナリデータを取得できるので,それを利用します。
 ・Exifデータはファイルの先頭に近い部分にあり,バイトデータパターンとして定められているので,先頭から1000バイト程度読み込み,それを16進byteデータの文字列として保存します。それを例えばByteDataStrとします。バイトパターンをこのByteDataStr内で検索して,値を求めます。

(2)Exifデータの確認。
 ・Exifデータは,SOIの直後APP1で始まります。これは16進byteで表すと,FFD8 FFE1となり,これがあれば,Exifデータがあることになります。

(3)エンディアンの確認。Exifデータはエンディアンに依存します。ExifデータのエンディアンはTIFF Headerに書かれていますが,それは,Exif識別コード(457869660000)の次の2Byteにあります。これが
 ・4D4Dならば,ビッグエンディアン,4949ならばリトルエンディアンです。

(4)Orientation値を求める。
 ・ビッグエンディアンの場合は,0112 0003 00000001 000?0000 のパターンで,?の部分に値(1~8)が入る。
 ・ リトルエンディアンの場合は,1201 0300 01000000 0?000000 のパターンで?の部分に値(1~8)が入る。

上の考えで,Orientation値を求めることができますので,これを使って,回転補正のデモプログラムを作ってみました。以下に紹介します。

Orientation.tbtという名前にしました。このプログラムは,ver. 1.62 で動作します。起動すると,

となります。参照ボタンを押すと,Open画面になり,例えば次のようになります。

ここで,開くボタンを押すと,Sample.jpgがLoadされて,

と表示されます。画面が横向きになっています。Orientaion情報が上に表示されています。このファイルは,ビッグエンディアンでOrientaion値6です。この意味が下に表示されています。時計回りに90度回転させれば良いの意味です。回転補正ボタンを押すと,

となります。このプログラムはここにあります。約150行ほどのプログラムで,

Orientation値を求める部分は30行程度です。RotateFlipの使い方の例にもなっています。

ResizeImage

 先日 tbasic Ver.1.62を公開しました。今回,このバージョンに対応したプログラムを作ってみましたので,それを紹介します。

 tbasicのVer.1.6での更新内容は主として,動作環境の整備でしたが,グラフ関係で

ResizeImageコマンド

も追加しました。このコマンドは,画像サイズの拡大縮小を行うコマンドです。また,Ver.1.61では,利用可能最大グラフ画面が4500×4500ピクセルになりました。また,Ver.1.62では,更にいくつかの機能の改良を行いました。

 近年の携帯のカメラの高度化により,1200万画素も標準的ともいわれます。これはピクセルで言えば,4000×3000の解像度を意味します。このような状況に対応できるように,Ver.1.61では,最大4500×4500のグラフ画面を利用可能へと修正しました。これで,4032×3024の画像も扱えるようになります。
 しかし,これらの画像は普通は数メガバイト以上のメモリを必要としており,メールに添付したりして利用するには大きすぎます。例えば,次の画像は,私のケータイで撮影したものですが,画面上は小さく見えますが,4160×3120ピクセルの画像で5.29Mバイトの容量を持ちます。

画像1

 容量に余裕があれば,これをこのまま利用することも良いでしょう。しかし,場合によっては,メモリサイズをより小さくして,利用したいこともあるかもしれません。このような場合,画像サイズを小さくすれば,必要とするメモリも少なくなります。例えば,上の例でサイズを縦横4分の1にすると,1040×780ピクセルの画像でメモリは302Kバイトになります。これを以下に表示するとなります。このように表示すると見た目はあまり変わりませんが,画面サイズは1/16,メモリは1/17になりました。

画像2

 このように,必要に応じて,画面の縮小ができると便利です。
 画像1を画像2に変換する方法は色々あり,画像縮小ツールとしてwebで検索すれば多くのツールが見つかるでしょう。しかしここでは,tbasicを利用することを考えます。

(1) tbasicのプログラムを作成する。

 tbasicでグラフ処理に慣れた人であれば,ResizeImageコマンドを利用して,プログラムは簡単に作成できます。それは,グラフ処理の定型的方法:「画像のロード」,「画像処理」,「画像の保存」での,「画像処理」の部分で,ResizeImage処理をするだけです。詳細な説明は避けますが,例えば,次のプログラムで可能です。

ChDir GetProgramDir
' プログラム,画像のあるフォルダをカレントディレクトリに設定
SI$="IMGSample.jpg" :' 入力画像名
TI$="IMGResize.jpg" :' 出力画像名
IMS$=GetImageSize$(SI$) :' 入力画像サイズの取得
WidthG =Val(Left$(IMS$,4)) :' 画像幅
HeightG =Val(Right$(IMS$,4)):' 画像高さ
GScreen(WidthG,HeightG,WidthG/4,HeightG/4)
' 画像用スクリーンの設定,表示は1/4画面を使う
LoadPicture(SI$)
GStretch On :' 全体を縮小して表示
ReSizeImage(1/4) :' 画像を縮小
SavePicture(TI$) :' 画像を保存
End

上のプログラムでは,画像1の名前がIMGSample.jpg,画像2の名前がIMGResize.jpgとしています。

 これはこれで,実用上良いのですが,色々な画像に対して同様な処理を行うとすると,面倒と思うかもしれません。そこで,これらの処理を行う汎用的なプログラムを作ってみました。

(2) tbasic で画像縮小ツールを作成する。

 このプログラムは全体で200行を超えるものですが,ReSizeImage.tbtという名前にしました。プログラムは以下にあります。(ファイルはzip圧縮されています。)

 実は,このプログラムの動作には,tbasic 1.62以上が必要です。ResizeImageコマンドは,1.6でサポートされましたが,このプログラムでは,1.61,1.62で新たにサポートされた機能を使っています。
 
 プログラムの骨格は上のものと本質的に同じですが,コントロール画面を利用したメニューを使って操作します。起動直後は次のようになります。

ここで,「読み込み」をクリックすると,

ダイアログが開き,対象とする画像を選択します。選択すると,画像の大きさを判定し,それに応じたグラフ画面が開かれます。大きな画像の場合は,800×800の画面を開き,縮小して表示します。画像1のファイルを選択した場合は,800×800の画面が開かれ,次のようになります。

この状態で,例えば1/4として,縮小率のボックスに1/4を入力して,Enterキーを押すと,メニュー部分が次のようになります。

画像ファイルの解像度が,4160×3120ですが,縮小率1/4で縮小すると,解像度が1040×780になると示されています。
 ここで,変換実行,保存とすることで,画像2のファイルが保存されます。プログラムは少し長いですが,一度作成すれば,色々な画像の縮小が簡単にできるので,使い道はあるのではと思います。 

binary viewer

tbasicが1.60になりました。これを機会にそのsamplesとして,いくつかのプログラムを作りました。ここではその中から,binary viewer を紹介します。プログラムの名前は,「Tiny Binary Viewer」で,ファイル名は「tbview.tbt」です。tbasic set 1.60 のsamples\Advancedの中にあります。

1.60では新たに,バイナリファイルの読み書きができる ReadAllBytes,WriteAllBytes関数・手続きをサポートしました。この使い方は,Tiny Basic for Windows ファイル操作編(2023年08月版)10節に書いてあります。簡単に言えば,ファイルの内容をバイト列として,読み書きするものです。
この機能を使えば,原理的には,バイナリエディタを作ることも可能ですが,利用目的が思いつかないので,今回は,binary viewerを作ることにしました。バイナリビューアはすべてのファイルのバイト内容を表示するもので。テキストファイル,画像ファイル,文書ファイル,実行ファイルなど,標準的な起動・開く方法とは異なった形式の表示を得ることができます。そして種々のファイルの舞台裏をつぶさに見ることができます。
今回色々なエンコーディングについて調べるときに,それらの内容の確認をするためには,テキストファイルをバイナリとして確認する必要がありました。そして, ユニコードへ(2023年8月版)を書く際に,実際にtbview.tbtを確認用として使いました。

tbview.tbtの起動画面は次です。

ここで,表示は16進表示,10進表示,アスキー表示が可能です。16,10進表示は0~255までの数値ですが,アスキー表示は,制御コードと印刷可能範囲で記号・数値・アルファベットが表示されます。
例えば,「1+1の計算」と記入したテキストファイルをシフトジスでファイル名「1足す1SJIS.txt」として保存して,この内容をバイナリビューアで表示させると,次のようになります。16進表示です。

見ると,ファイル内容は「31 2B 31 82 CC 8C 76 8E 5A」です。この結果は,ユニコードへ(2023年8月版)の23ページでシフトジスでの計算結果に合致します。
同様に,「1+1の計算」と記入したテキストファイルをエンコーディングEUCでファイル名「1足す1EUC.txt」として保存して,この内容をバイナリビューアで表示させると,次のようになります。

見ると,ファイル内容は「31 2B 31 A4 CE B7 D7 BB BB」です。この結果は,ユニコードへ(2023年8月版)の27ページでEUCでの計算結果に合致します。

プログラムは全体で,200行弱ですが,コントロール画面の設定がかなりの部分を占めています。実際,ファイルを指定して,その内容を表示するだけなら,数10行のプログラムで可能です。


工夫の余地はありますが,一応のツールとして使うことができます。興味があったらお試しください。

1 2 3