![見出し画像](https://assets.st-note.com/production/uploads/images/53999927/rectangle_large_type_2_21fafd2154a9ae846acdeef3b5a6a991.png?width=1200)
JPG画像の解像度(DPI)変更アプリの開発(3)ー製造(Ⅰ)
前回までの概要設計、詳細設計、プログラム設計を通して、作りたいアプリのイメージができた時点で、実際にアプリを作成していきます。
アプリを作成(作る)することを、製造又は実装といいます。アプリを作るってことは、実際にプログラミングするということです。
狭義的には、プログラミングすることを「開発する」って言ってる場合もありますが、実は開発とは設計、製造、テストから製品になるまでのすべてが含まれていて、単純にプログラミングすることだけではありません。
なので、プログラミング段階と設計段階を分けるために、プログラム作成することを製造、実装と呼びます。
DPI Changerアプリを製造する前に、実はもう一つの問題が残っています。
「JPG画像のDPI値を変更する方法」について、やり方がまだ分からないままです。
設計書には「指定されているフォルダ内のすべてのJPG画像をぐるぐる回しながら、DPI値を画面上で指定した値で書き換える」って言ってたんだけど、まあ、やりたいことは分かったんだけど。。。まあ、画面も一応詳細設計書に載ってるのでC#のwinform方式で作ればいいんだけど。。。(と思いながら)いざ手を動かしてプログラミングしようとした途端、「JPG画像のDPI値をどうやって書き換えるんだろう...」に気づくでしょう。
これは今回開発するDPI Changerアプリの肝になる技術問題です。
技術問題なので、プログラミングする前に調査・勉強し、実現方法を導き出すべきです。
この技術問題が解決できないと、DPI Changerアプリの開発はできないことになります。
プログラミングって、ただ人間が思っている流れ通りにコード(ソースコードともいう。プログラミング言語の規則に則って書いた、パソコンが見て理解できる箇条書きの手続き命令集のこと)を書くことだけなんで、画像のDPI値をどうやって変更できるかが分からないと(人間が先ず実現方法が分からないと)、コードも書けないことになります。
この技術問題を誰が解決すべきかというと、プログラマーと設計者とでどっちでもよいわけです。
技術レベルの高いプログラマーがいたら、その人が自力で調査しやり方を考えることができます。その場合は、プログラマーに任せばいいだけの話です。
逆にそういったレベルの技術者がいないと、設計者がやり方を考えて、詳細設計に添付してもよいです。
どっちにせよ、この技術問題は先に解決しないとアプリは作れませんよね。
基本、設計者(システムエンジニア、SE)はシステムを完成させる責任を持っているので、システム全体の設計に注力すべきだと思い、(筆者の場合は)このような技術問題はできるだけ技術者(いわゆるプログラマー)が解決してほしい考え方です。
ということで、この技術問題を「製造」段階で取り上げています。
実際の開発現場では、設計者とプログラマーとを、はっきり責任を分けることではなく、みんな一緒に力合わせてプロジェクトを完成させるのが目的です。このような技術問題があったら一緒に考えたりするでしょう。
一般的に、設計者(SE)も技術者出身が多く、忙しくない場合は技術問題に興味を持ったりします。
本シリーズでは「DPI Changerアプリを作る」だけではなく、このシリーズを契機にシステム開発の流れとか実際IT現場での作業に関して大体イメージができたら幸いです。
これからは技術問題の調査方法について解説していきます。
JPG画像のDPI値を変更する方法の考え方
下記のように、windowsでは簡単にJPG画像のDPI値を参照することができます。ただしDPI値を変更する手段は提供してくれてないので、自分でプログラミングするしかないのです。
JPG画像のDPI値
上記画像からみるとDPI値は画像ファイル内に書き込まれていることが分かります。
又DPI値って「水平方向の解像度」と「垂直方向の解像度」といった2つの値があることが分かります。
ということはJPG画像ファイル内の、上記「水平方向の解像度」と「垂直方向の解像度」の値(数値)が保存されている位置を見つけて、その位置の数値を画面から指定された数値に書き換えれば、JPG画像のDPIを変更したことになりますね。
ついでに、一番肝心なことは、「JPGフォーマット(構造)の画像ファイルを分析し、水平方向・垂直方向の解像度数値が保存されている位置(場所)を特定する」ことになります。
「書き換えるべき位置の探し方」(今回の技術問題の核)が分かったら、プログラムでその位置を探し、値を書き換えるといったソースコードを書けば実現できることになります。
実際にプログラミングする前に必ず下記の内容を理解したほうがよいでしょう。そうじゃないと、次回解説するプログラムが理解しにくいかもしれません。
JPGフォーマット画像ファイルの分析
調査する方向性が決まったら、これからはもう難しくありません。
JPGフォーマットの画像ファイルを分析するためには、まず「JPG画像のフォーマット(ファイル中身構成)」を理解する必要があります。
JPG画像のフォーマットって世界的な標準になっているので、下記wikipediaを検索すると、「JPEG File Interchange Format」というタイトルで「File format structure」の部分に詳しく説明してくれています。
JPEG File Interchange Format (wikiより)
グーグル先生に「JPEGファイルフォーマット」を聞けば、公式ドキュメントとか色んな技術者の方が書いた説明ページが英語、日本語も含め沢山検索できます。
JPEG画像関連の技術ドキュメント(説明書)ページを見ながら、JPG画像フォーマットを勉強・理解し、DPI値の場所を特定する方法を見つけ出す必要があります。
このような調査能力も将来フリーランサーになりたい方には必須のスキルになるでしょう。
ホームページの内容は載せられないですが、検索結果ページは下記のような感じです。色々見ながらJPG画像フォーマットを勉強し、DPI値の場所を特定するやり方確認してください。
JPEG画像フォーマット関連資料
基本的にはwikipediaページに詳しく説明されていますが、よく分からなかった場合はたくさんの方が自分のブログで解説しているのでいろいろ検索してみればよいでしょう。
これからは筆者が調査、分析したDPI値を位置を特定する方法を解説していきます。(技術者の皆さんも自力で研究してみてください)
① JPGフォーマットファイルであることを判定する方法
先ずは「ファイルがJPG画像ファイルであるかどうか」の判定ですね。DPI ChangerはJPG画像しかサポートしないので(概要設計で既に決まっている)、ファイルがJPG画像でない場合はスキップするようにします。
これはwikipediaページで説明しているので、簡単に判定できます。
ファイルをバイナリ形式で開くと、先頭2バイトが「FFD8」で、末尾2バイトが「FFD9」だったら、JPG画像のフォーマットであることが分かります。
図をみると、「SOI」セグメント、「EOI」セグメントという言い方をしていることが分かるでしょう。JPG画像フォーマットでは、「セグメント」という概念を取っていることがわかります。
下記のように実際にJPG画像をバイナリエディターで開いて検証しながら理解していくとより理解しやすいでしょう。
C#ではバイナリ方式でファイルを開き、先頭2バイト、末尾2バイトを取得し、それぞれ「FFD8」、「FFD9」になっているかどうかをチェックするソースコードで判定できます。
このように、JPGフォーマットに関する仕様書を公式サイトから取得し、理解しながら今回DPI値の保存されている位置を特定していきます。
次は、
② 「JFIF」フォーマットの判定
JPG画像には大きく下記の2種類のフォーマットが存在します。
・JFIF形式(フォーマット)
・EXIF形式(フォーマット)
DPI Changerアプリは上記2種類のフォーマットをサポートするので、それぞれのフォーマットを判定し、仕様に合った方法でDPI値の位置を特定する必要があります。
JFIFは比較的に簡単なフォーマットになります。下図がJFIFフォーマットの定義情報になります。
wikipediaのドキュメントから
JPG画像の始まりを表している「FFD8」の後ろにすぐ「FFE0」という2バイトがついていると、これはJFIF形式であることがわかります。
図から見ると、FFE0はAPP0セグメントであることが分かりますね。
ちなみに、EXIFはAPP1セグメントになります。
「FFE0」の後ろの2バイトはAPP0セグメントの長さですが、今回のDPI値とは関係ないのでパスします。
その後に4A、46、49、46、00という5バイトが必ず付いてることになります。これはじつはJFIFのASCIIコードになります。これがない場合はフォーマット異常と判断します。
JFIFは必ずAPP0セグメントで定義しているので、識別子が「JFIF」になってない場合はフォーマットエラーと判断します。
DPI Changerは上記の方法でJFIF形式の画像フォーマットを判定し、仕様定義に則って「水平方向解像度」、「垂直方法解像度」の位置を特定し、該当位置の数値を画面で指定したDPI値に書き換えます。
筆者が整理した JFIF形式のフォーマットのDPI値の位置図は下記の通りです。
上記のように JFIF形式のフォーマットのDPI値の位置は簡単に特定することができます。
③ 「EXIF」フォーマットの判定
EXIF形式はJFIF形式よりもっと複雑になりますので、よく理解してから分析する必要があります。
と言ってもすべてを理解するのではなく、DPI ChangerアプリではDPI値を書き換えるだけなので、EXIF形式の中のDPI値の位置を特定できればよいです。
ITの勉強には技術内容が沢山あるので誰もすべてを勉強したり、完全に把握したりできないものです。例え時間かけてすべてを勉強したとしても、日々その膨大な内容すべてを使うわけではないので、時間が経ったら徐々に忘れていくでしょう。それは勉強の意味がなくなります。
IT技術者達は日々勉強する必要があるんですが、あくまでも仕事に必要なもの、将来自分がやりたいことに必要な知識だけを勉強しているのです。残りは実際の仕事で必要になった時又勉強したりします。
今回もDPI値を変えるだけなので、複雑なEXIF形式をすべて理解しようとせず、DPI値が保存されている場所だけ見つけたらアプリが作れるのです。
先ずは定番の通り、「EXIFファイルフォーマット」のようなキーワードでグーグル先生に聞きます。(IT業界でグーグル先生は重要な存在です)
EXIF形式の説明ページがたくさん検索されるので、EXIF形式について勉強していきます。
先ずは公式サイトのドキュメントを探し、英語ばかりで分かりにくい場合はほかの日本語の文章も沢山あるので、読みながら勉強したほうがよいでしょう。
このように、解決すべき技術問題にぶつかったら自力で調査、分析しながら解決に至る能力もITでは必要になります。将来キャリアアップにも繋がる必須の能力になります。
筆者は一般社団法人カメラ映像機器工業会から下記のEXIFフォーマット関連のドキュメントを見つけました。
〇 画像ファイルフォーマット企画 Exif 2.3 (日本語版)
TIFFは画像の種類で(現在はあんまり見られなくなった)、中身はEXIF形式の仕様になっているので、JPGのEXIF形式と同じになります。詳しい情報はwikipediaを検索してください。
下記のように先ずはEXIF形式ファイルの全体のイメージとJFIF形式ファイルの全体イメージ図を比較してみましょう。
EXIF 、JFIFの比較図
上記の図で分かりますが、EXIFもJFIFもJPEG画像に所属しているので、JPEG画像であるかどうかの識別子(SOI, EOIセグメント)は同じですね(「① JPGフォーマットファイルであることを判定する方法」をご覧ください)。
違うのはEXIFはSOIセグメントの後ろにAPP1セグメントがついてくるんですが、JFIFはAPP0セグメントであることです。「② 「JFIF」フォーマットの判定」でも分かるように、APP0の中身は簡単にDPI値の位置が探せましたが、APP1はより複雑になるので、これから少しずつ解析していきます。
このように、形式が何種類かある場合はお互いに比較したりすると理解しやすくなります。
ー筆者のノーハウ
EXIF形式の画像はSOIセグメント(「FFD8」)の後ろにすぐ「FFE1」が付いてきます。これでEXIF形式だと判定できます。
実はこの言い方は厳密ではありません。筆者も色々な画像でアプリをテストしながら偶然発見したのですが、稀に SOI + APP0 + APP1の画像(APP0セグメントとAPP1セグメントの両方が存在)もあるようです。次回のプログラミング記事で取り上げますが、こんな場合はAPP1を優先し、EXIF形式だと判定しています。
続いては2バイトの「セグメント長」、そのあとには必ず「45、78、69、66、00、00」の6バイトが続きます。これは「Exif」という文字と2つのASCIIの0で、Exif形式の識別子にもなります。
続いて2バイトの「バイトオーダー」と、そのあとに固定値の「00、2A」とその後に4バイトの「IFD0へのオフセット」値が続きます。
上記の記述は読んでも分からないに決まってるでしょう。「なんじゃこりゃ!」になると思いますが、このように何等かのフォーマット、ファイル構造などの資料を勉強する場合は、「構造図を描くと理解しやすくなります」。
筆者はいろいろ資料みながら、自分でも構造を描いたりします。Exif形式の構造図は下記のようになります。
上記がEXIF形式のファイルのフォーマットの全般になります(①のJFIF形式よりは複雑ですね)。
この形式についても、今回のDPI Changerアプリを開発するためには、すべてを理解する必要はなく、「解像度(DPI)の値がどこに書いてあるか」だけを分かればいいです。
DPI値の位置を探す前に、まず理解しておくべきことを下記に解説します(プログラム開発時に重要です)。
④ バイトオーダー
画像や音声、動画などをファイルに書き込む場合、欠かせないのが「長さ」を表現するバイトです。
1バイト(byte) = 8ビット(bit)なので、数値を保存する場合、1バイトでは最大値255(0~255)しか保存できません。256を超える数値を保存するにはマルチバイト(1個以上のバイト)が必要になります。
長さの数値を保存するときは256以下だと1バイトで、もっと多きい数値は2バイトとか4バイトとかで保存します。
例えば、数値:50000を4バイトで書き込んでみましょう(1バイトでは50000を保存できないことは理解したでしょう)。
数値:50000
2進数: 1100 0011 0101 0000
16進数: C 3 5 0 つまり 0xC350
※2バイトだけで50000を表現できます。4バイトで表示したい場合は、先頭2バイトにすべて0を設定すればいいので、
0x0000C350になります。( 00 00 C3 50 で4バイトです)
2進数、16進数などの説明は割愛します。
この50000という数値の16進数0x0000C350をファイルに4バイトで書き込んでみましょう。
順番通りに書き込む方法と、逆順で書き込む方法という2種類の書き方があることに気づくでしょう。
つまり、順番通り書き込むと 00 00 C3 50 の順になるし、逆順で書き込むと 50 C3 00 00 になりますね。
「すべて順番通り書き込めばいいじゃん!なんでそんなに面倒くさく考えるの?」と思うでしょう。それはパソコンの世界が人間の世界とは異なるからです。
人間が見て分かりやすいからといって、パソコンにとっても簡単に処理できるとは限りません。
逆に、人間にとっては面倒くさくてもパソコンが処理するにはすごく簡単になることもあります。
この「順番通り書き込む」と「逆順で書き込む」には、技術的名前があって、それぞれ「BigEndian」、「LittleEndian」と呼びます(下図参照)。
Exif形式の中に、「数値はすべて順番通りに書いてるよ」とか「逆順で書いてるよ」と教えてくれる識別子があって、この識別子が「バイトオーダー」です。
下図のように、公式技術ドキュメントを読むと、 バイトオーダーが 「II(4949)」の場合はLittleEndian(上図のb)で、バイトオーダーが「MM(4D4D)」の場合はBigEndian(上図のa)になることが分かりますね。
上図の「Offset of IFD」はオフセットを表す数値で、4バイトになってますから、この4バイトもバイトオーダーによって順番が決まることになります。
下記は実際のEXIF形式の画像のバイナリデータであり、バイトオーダーが「MM(0x4D4D)」になっているので、数値項目はすべてaのBigEndianであることが分かります。
注: 0x0001 (十進数の1)と 0x0100(十進数の256)は全然違う値なので、バイトオーダーは非常に重要な情報になります。バイトオーダー判定を間違えると不具合になります。
ここまで理解できたら、もう残りはDPI値の位置を探すだけですね。
EXIF形式には複数のIFD(下図の赤枠)というブロックが並んでおり、DPI値はこのようなIFDの中で探します。
上図は筆者が公式ドキュメントを読みながら描いた図であり、IFDの詳しい内容は公式ドキュメントをベースにしています。
⑤ IFDの構造、フィールドの構造
公式ドキュメントには下記のようにIFDの構造について説明しています。
上記公式ドキュメントは分かりずらいですね。でもITの仕事をする場合、公式ドキュメントを読む・勉強するスキルも必要です。
図で表現すると、上記IFD、フィールド(12バイト固定)の構造は下記のようになります。
IFD、フィールドの構造図
EXIF形式には複数のIFDブロックが存在し、IFDブロックの中には複数のフィールドブロック(12バイト)が存在する感じです。
IFDはフィールドブロックの集まりのような感じです。
「タイプ」は2バイトの数値、「カウント」と「値へのオフセット」は4バイトの数値であることが分かりますね。こちらも複数バイトの数値なので、バイトオーダーが非常に重要になりますね。
ここで先にネタバレするんですが、上記のタイプの値:5について注意してください。あとで探したDPI値のタイプが5になります。タイプが5であるということは、DPI値が分数の形で設定されているということですね(なんで又面倒くさく保存しているんだ!)。
タイプが5の値は、LONG値2個で表現する、と書いてあります。(LONGってタイプ4から見ると4バイトの整数なので)8バイトが並んでいて、最初4バイトの数値は分子で、次の4バイトの数値は分母になります。
後で探し出したDPI値は上記のように、分数の値になります。(面倒くさいけど。。。世界的基準がこうなってるので。。。しょうもねぇ。。。)
ここまで理解できたら、DPI値はもうすぐです。DPI値はこのようなフィールドの中のタグで識別できます。
⑥ EXIF形式のDPI(解像度)値
フィールドの先頭2バイトは「タグ」になっていて、このタグの値が世界的に決まっています。すべてはこのタグで判断することになります(下図参照)。
上記赤枠に注目してくだだい。「画像の幅の解像度」、「画像の高さの解像度」がありますね。これがDPI値になります。
JFIF形式の場合は「水平方向の解像度」と「垂直方向の解像度」という言い方したんですが、EXIF形式では「画像の幅の解像度」、「画像の高さの解像度」と書いてますね。(多分翻訳の問題でしょう。意味は同じです)
ちなみに英語版のドキュメントには下記のように、「XResolution」、「YResolution」と書いてます。x方向(横)、y方向(縦)の意味ですね。
番外の話ですが、青色の枠をご覧ください。撮影した「場所(GPS)」や「機器メーカー名」とか「撮影著作権者」などがありますね。このような情報も画像内に書き込めるってことですね。画像の著作権を保護するようなアプリはこのような情報を読むのです。
このように、技術ドキュメントを読んだりするときは必要なものだけではなく、「このような情報もあるんだ。。。」程度でよいからほかの情報も少し覚えたりすると、いざそういうニーズが出た場合にはすぐ対応できるようになります(=技術レベルが高いという印象を残せます)。
これでEXIF形式の画像のDPI値(横方向のDPI値、縦方向のDPI値)がもう探せることになりますね。
「011A」タグを見つけたら横方向のDPI値で、「011B」タグを見つけたら縦方向のDPI値であることが分かります。
そして、タイプは5になっているはずなので、8バイトの長さで分数の形式で値を表現していますということが分かります。
1つのJPG画像には横方向解像度、縦方向解像度がそれぞれ1つしかないことに決まってるので、値の個数は1固定になります(下図参照)。
EXIF形式の解像度値特定方法
「値へのオフセット」は、実際のDPI値の位置なので、データ部にいって指定された場所から8バイト取得すれば、前4バイトは分子、後ろ4バイトは分母になります(分子 / 分母 = DPI値)。
はい、お疲れ様でした。やっとDPI値の位置が特定できるようになりましたね。
ここまできたら、もうDPI Changerアプリは開発できるようになります。
注意:
EXIFで一番重要なのが「バイトオーダー」ですね。複数バイトで1つの数値を表す場合は必ずこのバイトオーダーで指定している順序通り数値に変換すべきです。
もう一つは、「値へのオフセット」の数値は、JPG画像ファイルの先頭位置からのオフセットではなく、「基準位置」からのオフセットであることです。これ注意しないと、せっかくDPI値の場所を見つけたのに、最後1っ歩で間違ってしまいます。
とりあえず、「xxxオフセット」だとしたらすべてが「基準位置」からのオフセットになります。これもすごく重要なポイントです。
まとめ
この記事では、JPG画像の2種類のフォーマットの解析方法について解説しました。なお、このJFIF形式、EXIF形式の判定方法・解析方法を基に、次回からはプログラミングしていきます。
実際にプログラミングする前に、必ず本記事の内容を理解しておくことをお勧めします。
なお、IT業界って常に勉強し続けなければ自分の知識がだんだん古くなってしまってどんどん競争力が弱くなっていくので、常に必要な知識をゲットし、自力で勉強する能力が大事になります。
その為には自力で色んな公式ドキュメントを解読するスキルが必須になります。
本記事を読んで単なるDPI値の探し方だけではなく、公式の技術ドキュメントの読み方などについてもイメージできたら幸いです。
最後に、JFIF形式とEXIF形式をまとめた図を載せます。2つのフォーマットを比較しながら見るともっと理解しやすいですね。
(JFIF、すごく簡単。。 EXIF、ちょっと複雑。。。な感じ)
まあ、そんなに難しいもんでもないですけどね。。。
画像の色データを圧縮するところがもっと複雑で難しいでしょう。今回のDPI Changerアプリには関係ないですけどね。
では、バイバイ! Have a nice day!
この記事が気に入ったらサポートをしてみませんか?