仕事の準備
いつものように、まずターミナル ウィンドウを開いてコマンドを実行します。update50
アプリケーションがすでに最新であることを確認します。開始する前に、これに従って cd ~ / workspace
wget http://cdn.cs50.net/2015/fall/psets/4/pset4/pset4.zip
このタスクの ZIP アーカイブをダウンロードしてください。ここでlsを実行すると、 ~/workspaceディレクトリにpset4.zipというファイルがあることがわかります。次のコマンドを使用してそれを抽出します。 ls コマンドを再度実行すると、別のディレクトリが表示されることがわかります。これで、以下に示すように zip ファイルを削除できます。 pset4ディレクトリ を開いてls を実行し、ディレクトリに次のファイルが含まれていることを確認してください。 unzip pset4.zip
rm -f pset4.zip
cd pset4
bmp / jpg / questions.txt
フーダニットまたは「誰がこれをやった?」
デフォルトの Windows XP デスクトップ (https://en.wikipedia.org/wiki/Bliss_(image)) (丘と青空) を見たことがあるなら、BMP を見たことがあるでしょう。Web ページでは、GIF を見たことがあるでしょう。デジタル写真を見たことはありますか?それで、JPEGを見ることができてとてもうれしかったです。Mac でスクリーンショットを撮ったことがある人なら、おそらく PNG を見たことがあるでしょう。BMP、GIF、JPEG、PNG 形式についてインターネットで調べて、次の質問に答えてください。-
各形式は何色をサポートしていますか?
-
どの形式がアニメーションをサポートしていますか?
-
非可逆圧縮と可逆圧縮の違いは何ですか?
-
非可逆圧縮を使用する形式は次のうちどれですか?
-
FAT ファイル システムでファイルが削除されると、技術的な観点からはどうなりますか?
-
削除されたファイルを(高い確率で)確実に復元できないようにするにはどうすればよいでしょうか?
xxd -c 24 -g 3 -s 54 smiley.bmp
以下の内容が表示されるはずです。0000ff のすべてのインスタンスを再び赤色で強調表示しました。 左端の列の画像には、ファイル内のアドレスが表示されます。これは、ファイルの最初のバイトからのオフセットに相当します。それらはすべて 16 進数体系で与えられます。16 進数の 00000036 を 10 進数に変換すると、54 が得られます。つまり、 smiley.bmpの 54 番目のバイトを見ていることになります。24 ビット BMP ファイルでは、最初の 14 + 40 = 54 バイトがメタデータで埋められることを思い出してください。したがって、メタデータを確認したい場合は、次のコマンドを実行します。 smiley.bmpにASCII 文字が含まれているxxd -c 24 -g 3 smiley.bmp
場合、それらの文字はすべてのドットではなく、xxd の右端の列に表示されます。したがって、スマイリーは 8x8 ピクセルのサイズ (解像度) を持つ 24 ビット BMP (各ピクセルは 24 ÷ 8 = 3 バイトで表されます) です。したがって、各ライン (または「スキャンライン」と呼ばれる) は、(8 ピクセル) x (ピクセルあたり 3 バイト) = 24 バイトを占めます。この数値は 4 の倍数であり、行内のバイト数が 4 の倍数でない場合、BMP ファイルの保存方法が若干異なるため、これは重要です。したがって、フォルダー内の別の 24 ビット BMP ファイルである small.bmp には、緑色の 3x3 ピクセル ボックスが表示されます。画像ビューアで開くと、以下の画像に似ていますが、サイズが小さいだけであることがわかります。 したがって、 small.bmpの各行は(3 ピクセル) × (ピクセルあたり 3 バイト) = 9 バイトを占めますが、これは 4 の倍数ではありません。行の長さを 4 の倍数にするには、追加のゼロが埋め込まれます。 0 から 3 バイトの間で、各行を 24 ビット BMP 形式で埋めます (その理由がわかりますか?)。small.bmpの場合、(3 ピクセル) x (ピクセルあたり 3 バイト) + (パディングの 3 バイト) = 12 バイトであるため、3 バイトのゼロが必要です。これは実際には 4 の倍数です。このパディングを「確認」するには、次のようにします。以下をせよ。 -cにはsmiley.bmpとは 異なる値を使用するので、今回は xxd は 4 列のみを出力することに注意してください (緑色の四角形に 3 列、パディングに 1 列)。わかりやすくするために、00ff00 のすべてのインスタンスを緑色で強調表示しました。 対照的に、 large.bmpファイルには xxd を使用してみましょう。small.bmpとまったく同じように見えます。xxd -c 12 -g 3 -s 54 small.bmp
、解像度のみが 12x12 ピクセル、つまり 4 倍大きくなります。以下のコマンドを実行します。転送を避けるためにウィンドウを拡大する必要がある場合があります。 xxd -c 36 -g 3 -s 54 large.bmp
次のような内容が表示されます。 この BMP には余談がないことに注意してください。結局のところ、(12 ピクセル) × (ピクセルあたり 3 バイト) = 36 バイトとなり、これは 4 の倍数になります。 xxd 16 進エディターは、BMP ファイル内のバイトを表示しました。それらをプログラムで取得するにはどうすればよいでしょうか? copy.cには、BMP のコピーを部分的に作成することを唯一の目的とするプログラムが 1 つあります。はい、これにはcpを使用できます。しかし、cpはボディさんを助けることはできません。copy.c がこれを行うことを期待しましょう。それでは、ここで ls を./copy smiley.bmp copy.bmp
(適切なフラグを指定して) 実行すると、 smiley.bmpとcopy.bmpが実際に同じサイズであることがわかります。これが本当に本当かどうかもう一度確認してみませんか? このコマンドで画面に何も表示されない場合は、ファイルが実際に同一であることを意味します (重要: Photoshop などの一部のプログラムでは、一部の VMP の末尾に末尾のゼロが含まれています。私たちのバージョンのコピーではそれらが破棄されるため、使用しないでください)テスト用にダウンロードまたは作成した他の BMP をコピーする場合、コピーが元の BMP よりも数バイト小さくなる場合がありますのでご注意ください)。Ristretto の画像ビューアで両方のファイルを開き (ダブルクリック)、これを視覚的に確認できます。しかし、diff はこの比較をバイトごとに行うため、彼女の視野はあなたの視野よりも鋭いのです。このコピーはどのようにして作成されたのでしょうか? copy.c がbmp.hに関連していることがわかりました。確認しましょう: bmp.hを開きます。diff smiley.bmp copy.bmp
。そこには、Microsoft 独自の実装から適応された、すでに述べたヘッダーの実際の定義が表示されます。さらに、このファイルは BYTE、DWORD、LONG、および WORD データ型を定義します。これらは、Win32 (つまり Windows) プログラミングの世界で一般的に見られるデータ型です。これらは基本的に、(うまくいけば) すでによく知っているプリミティブのエイリアスであることに注意してください。BITMAPFILEHEADER と BITMAPINFOHEADER がこれらの型を使用していたことがわかりました。このファイルは、RGBTRIPLE という構造体も定義します。これは、青、緑、赤の 3 バイトを「カプセル化」します (これは、ディスク上で RGB トリプレットを探す順序です)。これらの構造体はどのように役立つのでしょうか? 要約すると、ファイルは単にディスク上の一連のバイト (最終的にはビット) です。ただし、これらのバイトは通常、最初のいくつかが何かを表し、次のいくつかが別のものを表す、というように順序付けされています。ファイルの「形式」が存在するのは、どのバイトが何を意味するかを定義する標準またはルールがあるからです。これで、ファイルを 1 つの大きなバイト配列としてディスクから RAM に読み取ることができます。そして、位置 [i] のバイトは 1 つのことを表し、位置 [j] のバイトは別の何かを表していることを思い出してください。しかし、これらのバイトの一部に名前を付けて、メモリからより簡単に取得できるようにしてはどうでしょうか? これはまさに bmp.h の構造が役に立ちます。ファイルを 1 つの長いバイトのシーケンスとして考えるのではなく、ファイルをより理解しやすいブロック、つまり一連の構造に分割すると考えます。smiley.bmpの解像度は 8x8 ピクセルなので、ディスク上で 14 + 40 + (8 × 8) × 3 = 246 バイトを占有することを思い出してください(これは ls コマンドを使用して確認できます)。Microsoft によると、ディスク上では次のようになります。 構造体のメンバーに関しては順序が重要であることがわかります。rgbtBlue は RGBTRIPLE で最初に定義されているため、バイト 57 は rgbtBlue (たとえば、rgbtRed ではありません) です。ちなみに、packed 属性を使用すると、clang がメンバーを (各メンバーの最初のバイトのアドレスが 4 の倍数になるように) 「ワード整列」しようとしないため、結果的にホールが発生することはありません。私たちの構造はディスク上にまったく存在しません。次へ移りましょう。bmp.h のコメントに従って、BITMAPFILEHEADER と BITMAPINFOHEADER に一致する URL を見つけます。注意、素晴らしい瞬間です。MSDN (Microsoft Developer Network) の使用を開始しています。copy.c をさらにスクロールする代わりに、いくつかの質問に答えて、コードがどのように機能するかを理解してください。いつものように、man コマンドはあなたの本当の友達であり、今では MSDN でもあります。答えがわからない場合は、グーグルで調べて考えてください。https://reference.cs50.net/ で stdio.h ファイルを参照することもできます。
-
main にブレークポイントを設定します (主行番号のあるルーラーの左側をクリックします)。
-
ターミナルタブで~/workspace/pset4/bmpに移動し、make を使用して copy.c をコピー プログラムにコンパイルします。
-
debug50 copy smiley.bmp copy.bmpを実行すると、右側にデバッガー パネルが開きます。
-
右側のパネルを使用してプログラムを段階的に進めていきます。bfとbiに注意してください。~/workspace/pset4/questions.txtで、次の質問に答えます。
-
stdint.hとは何ですか?
-
プログラム内でuint8_t、uint32_t、int32_t 、およびuint16_tを使用する意味は何ですか?
-
BYTE、DWORD、LONG、およびWORD にはそれぞれ何バイトが含まれますか(32 ビット アーキテクチャを想定)。
- BMP ファイルの最初の 2 バイトは何 (ASCII、10 進数、または 16 進数) にすべきですか? (ファイル形式を (高い確率で) 識別するために使用される先頭のバイトは、多くの場合「マジック ナンバー」と呼ばれます)。
-
bfSize と biSize の違いは何ですか?
-
負の biHeight は何を意味しますか?
-
BITMAPINFOHEADER のどのフィールドが BMP の色深度 (つまり、ピクセルあたりのビット数) を定義しますか?
-
fopen 関数が copy.c 37 で NULL を返すのはなぜですか?
-
コード内の fread の 3 番目の引数が 1 に等しいのはなぜですか?
-
bi.biWidth が 3 の場合、copy.c 70 のどの値がパディングを定義しますか?
-
fseekは何をするのですか?
-
SEEK_CURとは何ですか?
ボディさんの話に戻ります。エクササイズ:
Boddy 氏の描画を示すwhodunit.cというファイルに、whodunitというプログラムを作成します。うーん、何ですか?copy と同様に、プログラムはコマンド ライン引数を 2 つだけ取る必要があり、以下に示すようにプログラムを実行すると、結果は verdict.bmp に保存され、Boddy 氏の描画にノイズは発生しません。 以下のコマンドを実行して、この謎を解き始めることをお勧めします。 ボディ氏を助けるために何行のコードを書く必要があるかに驚かれるかもしれません。smiley.bmpには不必要な要素は何も隠されていないので、このファイルで自由にプログラムをテストしてください。これは小さいので、開発中にプログラムの出力と xxd の出力を比較できます (あるいは、 smiley.bmpに何かが隠されているのかもしれません。実際には違います)。ところで、この問題はさまざまな方法で解決できます。ボディ氏の絵を特定できれば、彼は安らかに眠るでしょう。Whodunit は複数の方法で実装できるため、 check50で実装の正確さをチェックすることはできません。楽しみが台無しになるかもしれませんが、アシスタントの解決策もフーダニット問題には利用できません。最後に、ファイル~/workspace/pset4/questions.txtで、次の質問に答えます。./whodunit clue.bmp verdict.b
cp copy.c whodunit.c
Whodunit? //ктоэтосделал?
サイズ変更
さて、次のテストです!size.c にsizeというプログラムを書いてみましょう。非圧縮 24 ビット BMP 画像のサイズを n ステップで変更します。アプリケーションは、正確に 3 つのコマンド ライン引数を受け入れる必要があります。最初の (n) は 100 以下の整数、2 番目は変更されるファイルの名前、3 番目は変更されたファイルの保存されたバージョンの名前です。ファイル。Usage: ./resize n infile outfile
このようなプログラムを使用すると、以下に示すように、small.bmp のサイズを 4 倍に変更する (つまり、幅と高さの両方を 4 倍する) ことで、small.bmp からlarge.bmp を作成できます。 簡単にするために、 copy.c を./resize 4 small.bmp large.bmp
再度コピーし、そのコピーにsize.c という名前を付けることでタスクを開始できます。しかし、まず最初に、 BMP サイズを変更することは何を意味するのか (infile サイズの n 倍は 232 - 1 を超えないと仮定できます) という質問に自問して答えてください。BITMAPFILEHEADER および BITMAPINFOHEADER のどのフィールドを変更する必要があるかを判断します。走査線フィールドを追加する必要があるか削除する必要があるかを検討してください。そして、はい、n の 0 から 1 までのすべての値を考慮するように求めているわけではないことに感謝してください。(ただし、興味があれば、これはハッカーの本の問題です ;))。ただし、n = 1 の場合、プログラムは正しく動作し、出力ファイルoutfile は元の infile と同じサイズになると仮定します。check50を使用してプログラムをチェックしますか? 次のコマンドを入力します。 ~cs50/pset4/resize
。 たとえば、
large.bmpヘッダー(xxd で許可されているものよりも使いやすい形式) を表示したい場合は、次のコマンドを実行する必要があります
~cs50/pset4/peek large.bmp
。ヘッダーは CS50 アシスタント ファイルのヘッダーと一致します。
~/workspace/pset4/bmpディレクトリ内でコマンドを実行できます(各コマンドが何を行うか考えてください)。
malloc を使用した場合は、メモリ リークを防ぐために必ず
freeを使用してください。
valgrindを使用してリークをチェックして みてください。
./resize 4 small.bmp student.bmp
~cs50/pset4/resize 4 small.bmp staff.bmp
~cs50/pset4/peek student.bmp staff.bmp
どうやって決めるの?
-
拡大する必要があるファイルを開き、拡大された画像を記録する新しいファイルを作成して開きます。
-
出力ファイルのヘッダー情報を更新します。画像は BMP 形式であり、そのサイズを変更しているため、新しいファイルのヘッダーを新しいサイズで書き込む必要があります。何が変わるのでしょうか?ファイル サイズと画像サイズ (幅と高さ)。
-
送信ファイルを 1 行ずつ、ピクセルごとに読み取ります。これを行うには、再びファイル I/O ライブラリと fread 関数に目を向けます。これは、読み取られるバイト、読み取る単一要素のサイズ、そのような要素の数、および読み取り元のファイルを含む構造体へのポインタを受け取ります。
-
指定されたスケールに従って各行を水平方向に増加させ、結果を出力ファイルに書き込みます。
ファイルはどのように書き込むのでしょうか? ファイルに書き込まれるデータが配置されている構造体へのインジケーター、要素のサイズ、要素の数、出力ファイルへのポインターを渡す関数 fwrite があります。ループを整理するには、すでに使い慣れたforループを使用できます。
-
差を埋める!ライン内のピクセル数が 4 の倍数でない場合は、「アライメント」 (ゼロ バイト) を追加する必要があります。アライメントサイズを計算するための式が必要になります。出力ファイルに null バイトを書き込むには、 fputc 関数を使用して、書き込む文字と出力ファイルへのポインターを渡します。
文字列を水平に引き伸ばして出力ファイルに配置を追加したので、配置を飛び越える必要があるため、出力ファイル内の現在の位置を移動する必要があります。
-
縦方向のサイズを大きくします。より複雑ですが、copy.cのサンプル コードを使用できます (copy.c は出力ファイルを開き、出力ファイルにヘッダーを書き込み、ソース ファイルから画像を 1 行ずつ、ピクセルごとに読み取り、それらを書き込みます)出力ファイルにコピーされます)。これに基づいて、最初にできることは、次のコマンドを実行することです: cp copy.csize.c
画像を垂直方向に引き伸ばすということは、各行を数回コピーすることを意味します。これを行うには、いくつかの異なる方法があります。たとえば、書き換えを使用して、1 ラインのすべてのピクセルをメモリに保存し、ループで必要な回数だけこのラインを出力ファイルに書き込みます。もう 1 つの方法は再コピーです。送信ファイルから 1 行を読み取り、それを出力ファイルに書き込み、位置合わせした後、fseek関数を送信ファイルの行の先頭に戻し、すべてを数回繰り返します。
-
メモリカードの内容が含まれるファイルを開きます。
-
JPEG ファイルの先頭を見つけます。このカード内のファイルはすべて JPEG 形式の画像です。
回復する
第 4 週の問題用紙を見越して、私はここ数日、デジタル カメラで 1 GB のコンパクトフラッシュ (CF) メモリ カードに JPEG 形式で保存された写真を眺めていました。私が実際にここ数日間 Facebook に費やしていたとは言わないでください。残念ながら、私のコンピュータースキルにはまだまだ不十分な点が多く、知らないうちに誤ってすべての写真を削除してしまいました。幸いなことに、コンピュータの世界では、通常、「削除」は「強制終了」と同じではありません。私のコンピュータはメモリカードが空になったと主張しますが、それが嘘であることはわかっています。 課題:これらの写真を復元するプログラムを ~/workspace/pset4/jpg/recover.cに作成します。うーん。さて、もう一つ説明があります。JPEG 形式は BMP よりも複雑ですが、JPEG には他のファイル形式と区別するのに役立つバイト パターンである「署名」があります。ほとんどの JPEG ファイルは、次の 3 バイトで始まります。0xff 0xd8 0xff
最初のバイトから 3 番目のバイトまで、左から右へ。4 番目のバイトは、0xe0、0xe1、0xe2、0xe3、0xe4、0xe5、0xe6、0xe7、0xe8、0xe8、0xe9、0xea、0xeb、0xec、0xed、0xee、0xef の組み合わせのいずれかになる可能性が高くなります。つまり、JPEG ファイルの 4 バイト目の最初の 4 ビットは 1110 です。写真が保存されているドライブ (メモリ カードなど) でこれらのパターンのいずれかを見つけた場合、これが JPEG ファイルの始まりである可能性があります。もちろん、どのディスクでこの問題が発生するかは全くの偶然である可能性があり、データ回復は正確な科学とは言えません。
GO TO FULL VERSION