Mempersiapkan pekerjaan
Seperti biasa, pertama buka jendela terminal dan jalankan perintah.update50
untuk memastikan aplikasi Anda sudah diperbarui. Sebelum memulai, ikuti ini cd ~ / workspace
wget http://cdn.cs50.net/2015/fall/psets/4/pset4/pset4.zip
untuk mengunduh arsip ZIP dari tugas ini. Sekarang jika Anda menjalankan ls Anda akan melihat bahwa Anda memiliki file bernama pset4.zip di direktori ~/workspace Anda . Ekstrak menggunakan perintah: Jika Anda menjalankan perintah lsunzip pset4.zip
lagi , Anda akan melihat bahwa direktori lain telah muncul. Sekarang Anda dapat menghapus file zip seperti yang ditunjukkan di bawah ini: Mari buka direktori pset4 , jalankan ls , dan pastikan direktori tersebut berisi rm -f pset4.zip
cd pset4
bmp / jpg / questions.txt
cerita detektif atau "Siapa yang melakukan ini?"
Jika Anda pernah melihat desktop default Windows XP (https://en.wikipedia.org/wiki/Bliss_(image)) (bukit dan langit biru), maka Anda pernah melihat BMP. Di halaman web, kemungkinan besar Anda pernah melihat GIF. Pernahkah Anda melihat foto digital? Jadi, kami senang melihat JPEG. Jika Anda pernah mengambil tangkapan layar di Mac, kemungkinan besar Anda melihat PNG. Baca di Internet tentang format BMP, GIF, JPEG, PNG dan jawab pertanyaan berikut:-
Berapa banyak warna yang didukung setiap format?
-
Format mana yang mendukung animasi?
-
Apa perbedaan antara kompresi lossy dan lossless?
-
Manakah dari format berikut yang menggunakan kompresi lossy?
-
Apa yang terjadi dari sudut pandang teknis ketika sebuah file dihapus dalam sistem file FAT?
-
Apa yang dapat dilakukan untuk memastikan (dengan kemungkinan besar) bahwa file yang terhapus tidak dapat dipulihkan?
xxd -c 24 -g 3 -s 54 smiley.bmp
Anda akan melihat apa yang ditunjukkan di bawah ini; Kami kembali menyorot dengan warna merah semua contoh 0000ff. Pada gambar di kolom paling kiri Anda dapat melihat alamat dalam file, yang setara dengan offset dari byte pertama file. Semuanya diberikan dalam sistem bilangan heksadesimal. Jika kita mengonversi heksadesimal 00000036 ke desimal, kita mendapatkan 54. Jadi, Anda sedang melihat byte ke-54 dari smiley.bmp . Ingatlah bahwa dalam file BMP 24-bit, 14 + 40 = 54 byte pertama diisi dengan metadata. Jadi jika ingin melihat metadatanya, jalankan perintah berikut: xxd -c 24 -g 3 smiley.bmp
Jika smiley.bmp berisi karakter ASCII , kita akan melihatnya di kolom paling kanan di xxd, bukan di semua titik itu. Jadi smiley adalah BMP 24-bit (setiap piksel diwakili oleh 24 8 = 3 byte) dengan ukuran (resolusi) 8x8 piksel. Setiap baris (atau disebut "Scanline") membutuhkan (8 piksel) x (3 byte per piksel) = 24 byte. Angka ini merupakan kelipatan empat, dan ini penting karena file BMP disimpan sedikit berbeda jika jumlah byte dalam baris bukan kelipatan empat. Jadi, di small.bmp, file BMP 24-bit lainnya di folder kami, Anda dapat melihat kotak hijau 3x3 piksel. Jika Anda membukanya di penampil gambar, Anda akan melihat tampilannya menyerupai gambar di bawah ini, hanya saja ukurannya lebih kecil. Setiap baris dalam small.bmp menempati (3 piksel) × (3 byte per piksel) = 9 byte, yang bukan merupakan kelipatan 4. Untuk mendapatkan panjang garis yang merupakan kelipatan 4, baris tersebut diisi dengan nol tambahan: antara 0 dan 3 byte kami mengisi setiap baris dalam format BMP 24-bit (bisakah Anda menebak alasannya?). Untuk small.bmp , diperlukan 3 byte nol, karena (3 piksel) x (3 byte per piksel) + (3 byte padding) = 12 byte, yang merupakan kelipatan 4. Untuk "melihat" padding ini, lakukan hal berikut. xxd -c 12 -g 3 -s 54 small.bmp
Perhatikan bahwa kami menggunakan nilai yang berbeda untuk -c dibandingkan dengan smiley.bmp , jadi xxd hanya menampilkan 4 kolom kali ini (3 untuk kotak hijau dan 1 untuk padding). Untuk lebih jelasnya, kami telah menyorot semua contoh 00ff00 dengan warna hijau. Sebagai kontras, mari gunakan xxd untuk file large.bmp . Tampilannya persis sama dengan small.bmp, hanya resolusinya 12x12 piksel, empat kali lebih besar. Jalankan perintah di bawah ini. Anda mungkin perlu memperluas jendela untuk menghindari transfer. xxd -c 36 -g 3 -s 54 large.bmp
Anda akan melihat sesuatu seperti ini: Harap dicatat, tidak ada penyimpangan dalam BMP ini! Lagi pula, (12 piksel) × (3 byte per piksel) = 36 byte, dan ini merupakan kelipatan 4. Editor hex xxd menunjukkan kepada kita byte dalam file BMP kita. Bagaimana kita bisa mendapatkannya secara terprogram? Di copy.c ada satu program yang tujuan hidupnya satu-satunya adalah membuat salinan BMP, sepotong demi sepotong. Ya, Anda dapat menggunakan cp untuk ini . Namun cp tidak akan bisa membantu Pak Boddy. Mari kita berharap copy.c melakukan hal ini, jadi ini dia: ./copy smiley.bmp copy.bmp
Jika sekarang Anda menjalankan ls (dengan flag yang sesuai), Anda akan melihat bahwa smiley.bmp dan copy.bmp memang berukuran sama. Mari kita periksa lagi apakah ini benar? diff smiley.bmp copy.bmp
Jika perintah ini tidak menampilkan apa pun di layar, berarti file tersebut memang identik (penting: beberapa program, seperti Photoshop, menyertakan angka nol di akhir beberapa VMP. Versi salinan kami membuangnya, jadi jangan khawatir jika menyalin BMP lain yang telah Anda unduh atau buat untuk pengujian, salinannya akan lebih kecil beberapa byte dari aslinya). Anda dapat membuka kedua file di penampil gambar Ristretto (klik dua kali) untuk mengonfirmasi hal ini secara visual. Tapi perbandingan ini berbeda byte demi byte, jadi penglihatannya lebih tajam dari Anda! Bagaimana salinan ini dibuat? Ternyata copy.c ada hubungannya dengan bmp.h . Mari kita pastikan: buka bmp.h. Di sana Anda akan melihat definisi sebenarnya dari header yang telah kami sebutkan, diadaptasi dari implementasi Microsoft sendiri. Selain itu, file ini mendefinisikan tipe data BYTE, DWORD, LONG, dan WORD, yang merupakan tipe data yang biasanya ditemukan di dunia pemrograman Win32 (yaitu Windows). Perhatikan bahwa ini pada dasarnya adalah alias untuk primitif yang (mudah-mudahan) sudah Anda kenal. Ternyata BITMAPFILEHEADER dan BITMAPINFOHEADER menggunakan tipe ini. File ini juga mendefinisikan struct yang disebut RGBTRIPLE. Ini “merangkum” tiga byte: satu biru, satu hijau dan satu merah (ini adalah urutan di mana kita akan mencari triplet RGB pada disk). Bagaimana struct ini berguna? Singkatnya, file hanyalah urutan byte (atau akhirnya bit) pada disk. Namun, byte-byte ini biasanya diurutkan sehingga beberapa byte pertama mewakili sesuatu, kemudian beberapa byte berikutnya mewakili sesuatu yang lain, dan seterusnya. File "format" ada karena kita memiliki standar, atau aturan, yang menentukan arti byte. Sekarang, kita cukup membaca file dari disk ke dalam RAM sebagai satu array byte besar. Dan kita ingat bahwa byte pada posisi [i] mewakili satu hal, sedangkan byte pada posisi [j] adalah sesuatu yang lain. Namun mengapa tidak memberi nama beberapa byte ini agar kita dapat lebih mudah mengambilnya dari memori? Inilah tepatnya yang dibantu oleh struktur di bmp.h. Daripada menganggap file sebagai satu rangkaian byte yang panjang, kita melihatnya dipecah menjadi blok-blok yang lebih mudah dipahami - rangkaian struktur. Ingatlah bahwa smiley.bmp memiliki resolusi 8x8 piksel, sehingga memakan 14 + 40 + (8 × 8) × 3 = 246 byte pada disk (Anda dapat memeriksanya menggunakan perintah ls). Berikut tampilannya pada disk menurut Microsoft: Kita dapat melihat bahwa urutan penting ketika menyangkut anggota struct. Byte 57 adalah rgbtBlue (bukan, katakanlah, rgbtRed) karena rgbtBlue didefinisikan dalam RGBTRIPLE terlebih dahulu. Ngomong-ngomong, penggunaan atributpacked kami memastikan bahwa clang tidak mencoba untuk "menyelaraskan kata" anggota (dengan alamat byte pertama setiap anggota adalah kelipatan 4), sehingga kita tidak mendapatkan lubang di dalamnya. struktur kami yang tidak ada di disk sama sekali. Mari kita lanjutkan. Temukan URL yang cocok dengan BITMAPFILEHEADER dan BITMAPINFOHEADER, sesuai komentar di bmp.h. Perhatian, momen yang luar biasa: Anda mulai menggunakan MSDN (Microsoft Developer Network)! Daripada menelusuri copy.c lebih jauh , jawablah beberapa pertanyaan untuk memahami cara kerja kode. Seperti biasa, perintah man adalah teman sejati Anda, dan sekarang juga MSDN. Jika Anda tidak tahu jawabannya, carilah di Google dan pikirkanlah. Anda juga dapat merujuk ke file stdio.h di https://reference.cs50.net/.
-
Tetapkan breakpoint di main (dengan mengklik di sebelah kiri penggaris dengan nomor baris utama).
-
Di tab terminal , buka ~/workspace/pset4/bmp , dan kompilasi copy.c ke dalam program penyalinan menggunakan make.
-
Jalankan debug50 copy smiley.bmp copy.bmp , ini akan membuka panel debugger di sebelah kanan.
-
Jalani program langkah demi langkah menggunakan panel di sebelah kanan. Catatan bf dan bi . Di ~/workspace/pset4/questions.txt , jawab pertanyaan:
-
Apa itu stdint.h ?
-
Apa gunanya menggunakan uint8_t , uint32_t , int32_t dan uint16_t dalam sebuah program?
-
Berapa banyak byte yang masing-masing berisi BYTE , DWORD , LONG , dan WORD (Dengan asumsi arsitektur 32-bit)?
- Berapa (ASCII, desimal atau heksadesimal) dua byte pertama file BMP? (byte awal, yang digunakan untuk mengidentifikasi format file (dengan probabilitas tinggi) sering disebut "angka ajaib").
-
Apa perbedaan antara bfSize dan biSize?
-
Apa yang dimaksud dengan biHeight negatif?
-
Bidang manakah di BITMAPINFOHEADER yang menentukan kedalaman warna dalam BMP (yaitu bit per piksel)?
-
Mengapa fungsi fopen dapat mengembalikan NULL di copy.c 37?
-
Mengapa argumen ketiga untuk takut dalam kode kita sama dengan 1?
-
Nilai apa di copy.c 70 yang mendefinisikan padding jika bi.biWidth adalah 3?
-
Apa yang dilakukan fseek?
-
Apa itu SEEK_CUR?
Kembali ke Tuan Boddy. Latihan:
Tulis sebuah program bernama whodunit dalam file bernama whodunit.c yang memperlihatkan gambar Pak Boddy. Hmmmm, apa? Seperti copy, program harus mengambil tepat dua argumen baris perintah, dan jika Anda menjalankan program seperti gambar di bawah ini, hasilnya akan disimpan di vonis.bmp, di mana gambar Pak Boddy tidak akan berisik../whodunit clue.bmp verdict.b
Izinkan kami menyarankan Anda untuk mulai memecahkan misteri ini dengan menjalankan perintah di bawah ini. cp copy.c whodunit.c
Anda mungkin terkejut dengan banyaknya baris kode yang perlu Anda tulis untuk membantu Pak Boddy. Tidak ada hal yang tidak perlu yang disembunyikan di smiley.bmp , jadi silakan uji program pada file ini. Ini kecil, dan Anda dapat membandingkan keluaran program Anda dan keluaran xxd selama pengembangan (atau mungkin ada sesuatu yang tersembunyi di smiley.bmp ? Sebenarnya tidak). Omong-omong, masalah ini dapat diselesaikan dengan berbagai cara. Setelah Anda mengidentifikasi gambar Tuan Boddy, dia akan beristirahat dengan tenang. Karena whodunit dapat diimplementasikan dalam berbagai cara, Anda tidak akan dapat memeriksa kebenaran implementasi dengan check50 . Dan biarkan hal itu merusak kesenangan Anda, tetapi solusi asisten juga tidak tersedia untuk masalah cerita detektif . Terakhir, di file In ~/workspace/pset4/questions.txt , jawab pertanyaan berikut: Whodunit? //ктоэтосделал?
mengubah ukuran
Nah, sekarang - tes selanjutnya! Mari kita menulis sebuah program bernama resize di resize.c . Ini akan mengubah ukuran gambar BMP 24-bit yang tidak terkompresi dalam langkah n. Aplikasi Anda harus menerima tepat tiga argumen baris perintah, dengan yang pertama (n) berupa bilangan bulat tidak lebih besar dari 100, yang kedua adalah nama file yang akan diubah, dan yang ketiga adalah nama versi tersimpan dari file yang dimodifikasi. mengajukan.Usage: ./resize n infile outfile
Dengan program seperti itu, kita dapat membuat large.bmp dari small.bmp dengan mengubah ukurannya menjadi 4 (yaitu, mengalikan lebar dan tinggi dengan 4), seperti yang ditunjukkan di bawah ini. Untuk mempermudah, Anda dapat memulai tugas dengan menyalin copy.c./resize 4 small.bmp large.bmp
lagi dan memberi nama salinan resize.c . Tapi pertama-tama, tanyakan pada diri Anda dan jawab pertanyaan-pertanyaan ini: apa artinya mengubah ukuran BMP (Anda dapat berasumsi bahwa n kali ukuran infile tidak akan melebihi 232 - 1) . Tentukan bidang mana di BITMAPFILEHEADER dan BITMAPINFOHEADER yang perlu Anda ubah. Pertimbangkan apakah Anda perlu menambah atau menghapus bidang scanlines . Dan ya, bersyukurlah karena kami tidak meminta Anda mempertimbangkan semua kemungkinan nilai n dari 0 hingga 1! (walaupun, jika Anda tertarik, ini adalah masalah dari buku peretas ;)). Namun, kami berasumsi bahwa untuk n = 1 program akan bekerja dengan benar dan file keluaran file keluar akan berukuran sama dengan file masuk asli. Apakah Anda ingin memeriksa program menggunakan check50? Ketik perintah berikut: ~cs50/pset4/resize
Nah, jika Anda ingin melihat, misalnya, header
large.bmp (dalam bentuk yang lebih mudah digunakan daripada yang dimungkinkan oleh xxd), Anda perlu menjalankan perintah berikut:
~cs50/pset4/peek large.bmp
Lebih baik lagi, jika Anda ingin membandingkan header dengan header file asisten CS50. Anda dapat menjalankan perintah di dalam direktori
~/workspace/pset4/bmp Anda (pikirkan apa yang dilakukan setiap perintah). Jika Anda menggunakan
malloc , pastikan menggunakan
free untuk mencegah kebocoran memori. Coba gunakan
valgrind untuk memeriksa kebocoran.
./resize 4 small.bmp student.bmp
~cs50/pset4/resize 4 small.bmp staff.bmp
~cs50/pset4/peek student.bmp staff.bmp
Bagaimana cara memutuskan?
-
Buka file yang perlu kita perbesar, dan juga buat dan buka file baru di mana gambar yang diperbesar akan direkam;
-
perbarui informasi header untuk file output. Karena gambar kita dalam format BMP dan kita mengubah ukurannya, kita perlu menulis header file baru dengan dimensi baru. Apa yang akan berubah? Ukuran file, serta ukuran gambar - lebar dan tingginya.
-
Kami membaca file keluar, baris demi baris, piksel demi piksel. Untuk melakukan ini, kita kembali beralih ke perpustakaan file I/O dan fungsi fread. Dibutuhkan sebuah pointer ke struktur yang akan berisi byte yang dibaca, ukuran elemen tunggal yang akan kita baca, jumlah elemen tersebut, dan pointer ke file yang akan kita baca.
-
Kami memperbesar setiap baris secara horizontal sesuai dengan skala yang ditentukan, dan menulis hasilnya ke file keluaran.
Bagaimana kita menulis file? Kami memiliki fungsi fwrite, yang dengannya kami meneruskan indikator ke struktur tempat data yang akan ditulis ke file berada, ukuran elemen, nomornya, dan penunjuk ke file keluaran. Untuk mengatur perulangan, kita dapat menggunakan perulangan for yang sudah kita kenal .
-
Isilah kekosongannya! Jika jumlah piksel dalam satu baris bukan kelipatan empat, kita harus menambahkan "penyelarasan" - nol byte. Kita memerlukan rumus untuk menghitung ukuran perataan. Untuk menulis byte nol ke file keluaran, Anda dapat menggunakan fungsi fputc, meneruskan karakter yang ingin Anda tulis dan penunjuk ke file keluaran ke dalamnya.
Sekarang kita telah meregangkan string secara horizontal dan menambahkan perataan ke file keluaran, kita perlu memindahkan posisi saat ini di file keluaran karena kita perlu melompati perataan tersebut.
-
Tingkatkan ukuran vertikal. Ini lebih rumit, tetapi kita dapat menggunakan kode contoh dari copy.c (copy.c membuka file keluaran, menulis header ke file keluaran, membaca gambar dari file sumber baris demi baris, piksel demi piksel, dan menulisnya ke file keluaran). Berdasarkan hal tersebut, hal pertama yang dapat Anda lakukan adalah menjalankan perintah berikut: cp copy.c resize.c
Meregangkan gambar secara vertikal berarti menyalin setiap baris beberapa kali. Ada beberapa cara berbeda untuk melakukan ini. Misalnya, menggunakan penulisan ulang, ketika kita menyimpan semua piksel dari satu baris ke dalam memori dan menulis baris ini ke file keluaran dalam satu lingkaran sebanyak yang diperlukan. Metode lain adalah menyalin ulang: setelah membaca baris dari file keluar, menulisnya ke file keluaran dan menyelaraskannya, kembalikan fungsi fseek ke awal baris di file keluar dan ulangi semuanya lagi beberapa kali.
-
Buka file dengan isi kartu memori.
-
Temukan awal file JPEG. Semua file di kartu ini adalah gambar dalam format JPEG.
pulih
Untuk mengantisipasi makalah soal Minggu ke-4, saya menghabiskan beberapa hari terakhir melihat foto-foto yang disimpan dalam format JPEG oleh kamera digital saya pada kartu memori CompactFlash (CF) 1 GB. Tolong jangan beri tahu saya bahwa saya sebenarnya menghabiskan beberapa hari terakhir di Facebook. Sayangnya, keterampilan komputer saya masih kurang, dan tanpa saya sadari, saya tidak sengaja menghapus semua foto! Untungnya, dalam dunia komputer, "dihapus" biasanya tidak sama dengan "dibunuh". Komputer saya bersikeras bahwa kartu memori sekarang kosong, tapi saya tahu itu bohong. Tugas: Tulis program di ~/workspace/pset4/jpg/recover.c yang akan memulihkan foto-foto ini. Hmmm. Oke, ini satu klarifikasi lagi. Meskipun format JPEG lebih kompleks daripada BMP, JPEG memiliki “tanda tangan”, pola byte yang membantu membedakannya dari format file lainnya. Kebanyakan file JPEG dimulai dengan tiga byte berikut:0xff 0xd8 0xff
Dari byte pertama hingga ketiga, dari kiri ke kanan. Byte keempat kemungkinan besar merupakan salah satu dari kombinasi berikut: 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,0xe8, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef. Dengan kata lain, empat bit pertama dari byte keempat file JPEG adalah 1110. Kemungkinannya adalah, jika Anda menemukan salah satu pola ini di drive tempat foto disimpan (seperti kartu memori saya), ini akan menjadi awal dari file JPEG. Tentu saja, Anda mungkin menemukan ini pada disk mana yang murni kebetulan; pemulihan data tidak dapat disebut ilmu pasti.
GO TO FULL VERSION