JavaRush /Blog Java /Random-VI /Harvard CS50: Bài tập tuần 4 (Bài 9 và 10)
Masha
Mức độ

Harvard CS50: Bài tập tuần 4 (Bài 9 và 10)

Xuất bản trong nhóm
Harvard CS50: Bài tập Tuần 4 (Bài 9 và 10) - 1

Chuẩn bị cho công việc

Như mọi khi, trước tiên hãy mở cửa sổ terminal và chạy lệnh. update50 để đảm bảo ứng dụng của bạn đã được cập nhật. Trước khi bạn bắt đầu, hãy làm theo cách này cd ~ / workspace wget http://cdn.cs50.net/2015/fall/psets/4/pset4/pset4.zip để tải xuống bản lưu trữ ZIP của tác vụ này. Bây giờ nếu bạn chạy ls bạn sẽ thấy rằng bạn có một tệp có tên pset4.zip trong thư mục ~/workspace . Giải nén nó bằng lệnh: Nếu chạy unzip pset4.zip lại lệnh ls , bạn sẽ thấy một thư mục khác đã xuất hiện. Bây giờ bạn có thể xóa tệp zip như hiển thị bên dưới: rm -f pset4.zip Hãy mở thư mục pset4 cd pset4 , chạy ls và đảm bảo thư mục chứa bmp / jpg / questions.txt

whodunit hoặc "Ai đã làm điều này?"

Nếu bạn đã từng nhìn thấy màn hình mặc định của Windows XP (https://en.wikipedia.org/wiki/Bliss_(image)) (những ngọn đồi và bầu trời xanh), thì bạn đã thấy BMP. Trên các trang web, rất có thể bạn đã nhìn thấy ảnh GIF. Bạn đã xem ảnh kỹ thuật số chưa? Vì vậy, chúng tôi rất vui khi được nhìn thấy JPEG. Nếu bạn đã từng chụp ảnh màn hình trên máy Mac, rất có thể bạn đã nhìn thấy PNG. Đọc trên Internet về các định dạng BMP, GIF, JPEG, PNG và trả lời những câu hỏi sau:
  1. Mỗi định dạng hỗ trợ bao nhiêu màu?

  2. Định dạng nào hỗ trợ hoạt ảnh?

  3. Sự khác biệt giữa nén lossy và lossless là gì?

  4. Định dạng nào sau đây sử dụng tính năng nén có tổn hao?

Những người nói tiếng Anh nên tham khảo bài viết của MIT . Nếu bạn nghiên cứu nó (hoặc tìm các tài nguyên khác trên Internet về việc lưu trữ tệp trên đĩa và hệ thống tệp), bạn có thể trả lời các câu hỏi sau:
  1. Điều gì xảy ra từ quan điểm kỹ thuật khi một tệp bị xóa trong hệ thống tệp FAT?

  2. Có thể làm gì để đảm bảo (với khả năng cao) rằng không thể khôi phục được các tệp đã xóa?

Và bây giờ - đến câu chuyện của chúng ta, câu chuyện trôi chảy trong nhiệm vụ đầu tiên của tuần thứ tư. Chào mừng đến với biệt thự Tudor! Chủ sở hữu bất động sản, ông John Boddy, đột nhiên rời bỏ chúng tôi, trở thành nạn nhân của một trò chơi mờ ám. Để tìm hiểu chuyện gì đã xảy ra, bạn phải xác định whodunit . Thật không may cho bạn (thậm chí còn không may hơn cho ông Boddy), bằng chứng duy nhất bạn có là tệp BMP 24-bit manh mối.bmp . Đó là nội dung của nó mà bạn nhìn thấy dưới đây. Ông Boddy đã cố gắng tạo và lưu nó vào máy tính của mình trong những giây phút cuối cùng. Tệp chứa hình ảnh whodunit ẩn giữa tiếng ồn đỏ . Bây giờ bạn cần nghiên cứu giải pháp như một chuyên gia kỹ thuật thực thụ. Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 2Nhưng trước tiên, một số thông tin. Có lẽ dễ dàng nhất khi coi hình ảnh là một lưới các pixel (nghĩa là các dấu chấm), mỗi pixel có thể là một màu cụ thể. Để đặt màu cho một điểm trong ảnh đen trắng, chúng ta cần 1 bit. 0 có thể đại diện cho màu đen và 1 có thể đại diện cho màu trắng như trong hình bên dưới. Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 3Do đó, hình ảnh được biểu diễn theo cách này chỉ đơn giản là bản đồ của các bit (bitmap hoặc bitmap, như người ta nói bằng tiếng Anh hoặc tiếng lóng). Với đen trắng, mọi thứ càng đơn giản càng tốt, nhưng để có được hình ảnh màu, chúng ta chỉ cần nhiều bit hơn trên mỗi pixel. Định dạng tệp (chẳng hạn như GIF) hỗ trợ "màu 8 bit" sử dụng 8 bit cho mỗi pixel. Định dạng tệp (ví dụ: BMP, JPG, PNG) hỗ trợ "màu 24 bit" sử dụng 24 bit cho mỗi pixel (BMP thực tế hỗ trợ màu 1-, 4-, 8-, 16-, 24- và 32-bit) . Trong BMP 24-bit mà Mr. Boddy sử dụng, cần 8 bit để biểu thị lượng màu đỏ, lượng tương tự cho màu xanh lá cây và 8 bit nữa để biểu thị lượng màu xanh lam trong mỗi pixel. Nếu bạn đã từng nghe nói về màu RGB thì chính là màu này (R=red, G=green, B=blue). Nếu giá trị R, G và B của một số pixel trong BMP là 0xff, 0x00 và 0x00 theo hệ thập lục phân thì pixel đó sẽ có màu đỏ thuần, vì 0xff (còn được gọi là 255 trong số thập phân) có nghĩa là "rất nhiều màu đỏ". " tại thời điểm đó, 0x00 và 0x00 có nghĩa tương ứng là “không có màu xanh lá cây” và “màu xanh lam cũng là số 0”. Dựa vào cách hình ảnh BMP của Mr. Boddy xuất hiện với chúng ta màu đỏ, có thể trực quan rằng "ngăn" màu đỏ có giá trị lớn hơn rõ ràng so với "ngăn" màu đỏ và xanh lam. Tuy nhiên, không phải tất cả các pixel đều có màu đỏ; một số pixel rõ ràng có màu khác. Nhân tiện, trong HTML và CSS (ngôn ngữ đánh dấu và biểu định kiểu hỗ trợ nó, được sử dụng để tạo trang web), các mô hình màu được sắp xếp theo cùng một cách. Nếu quan tâm, hãy xem liên kết: https://ru.wikipedia.org/wiki/Colors_HTMLđể biết thêm chi tiết. Bây giờ hãy tiếp cận vấn đề một cách kỹ thuật hơn. Hãy nhớ lại rằng một tập tin chỉ đơn giản là một chuỗi các bit được sắp xếp theo thứ tự nào đó. Tệp BMP 24 bit là một chuỗi các bit, cứ 24 bit trong số đó (gần như) xác định màu của pixel nào. Ngoài dữ liệu màu sắc, file BMP còn chứa siêu dữ liệu - thông tin về chiều rộng và chiều cao của hình ảnh. Siêu dữ liệu này được lưu trữ ở đầu tệp dưới dạng hai cấu trúc dữ liệu thường được gọi là "tiêu đề" (đừng nhầm với tệp tiêu đề C). Tiêu đề đầu tiên trong số này là BITMAPFILEHEADER, dài 14 byte (hoặc 14*8 bit). Tiêu đề thứ hai là BITMAPINFOHEADER (dài 40 byte). Sau các tiêu đề này là bitmap: một mảng byte, các bộ ba biểu thị màu của pixel (1, 4 và 16-bit trong BMP, nhưng không phải 24 hoặc 32, chúng có tiêu đề bổ sung ngay sau BITMAPINFOHEADER. Nó được gọi là mảng RGBQUAD, xác định “giá trị cường độ” cho từng màu trong bảng màu). Tuy nhiên, BMP lưu trữ các bộ ba này theo chiều ngược lại (chúng ta có thể nói giống như BGR), với 8 bit cho màu xanh lam, 8 bit cho màu xanh lục và 8 bit cho màu đỏ. Nhân tiện, một số BMP còn lưu trữ toàn bộ bitmap ngược, bắt đầu từ dòng trên cùng của hình ảnh ở cuối tệp BMP. Trong nhiệm vụ của mình, chúng tôi đã lưu VMR như được mô tả ở đây, đầu tiên là hàng trên cùng của hình ảnh, sau đó là hàng dưới cùng. Nói cách khác, chúng tôi đã biến biểu tượng cảm xúc một bit thành biểu tượng cảm xúc 24 bit bằng cách thay thế màu đen bằng màu đỏ. BMP 24 bit sẽ lưu trữ bitmap này, trong đó 0000ff đại diện cho màu đỏ và ffffff đại diện cho màu trắng; chúng tôi đã đánh dấu tất cả các trường hợp 0000ff bằng màu đỏ. Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 4Vì chúng tôi đã trình bày các phần này từ trái sang phải, từ trên xuống dưới nên bạn có thể nhìn thấy mặt cười màu đỏ trong các chữ cái này nếu bạn di chuyển ra xa màn hình một chút. Hãy nhớ lại rằng một chữ số trong hệ thống số thập lục phân đại diện cho 4 bit. Theo đó, ffffff trong hệ thập lục phân thực sự có nghĩa là 11111111111111111111111111 trong hệ nhị phân. Bây giờ, hãy chậm lại và đừng tiến xa hơn cho đến khi bạn chắc chắn hiểu lý do tại sao 0000ff đại diện cho pixel màu đỏ trong tệp BMP 24 bit. Trong cửa sổ CS50 IDE, hãy mở rộng (ví dụ: bằng cách nhấp vào hình tam giác nhỏ) thư mục pset4 và trong đó - bmp . Trong thư mục này, bạn sẽ tìm thấy Smiley.bmp , nhấp đúp vào tệp và bạn sẽ tìm thấy một biểu tượng mặt cười nhỏ 8x8 pixel ở đó. Trong menu thả xuống, hãy thay đổi tỷ lệ hình ảnh, chẳng hạn từ 100% thành 400%, điều này sẽ cho phép bạn xem phiên bản lớn hơn nhưng đồng thời "mờ" hơn của biểu tượng cảm xúc. Mặc dù trên thực tế, hình ảnh này sẽ không bị mờ ngay cả khi phóng to. Chỉ là CS50 IDE đang cố gắng giúp đỡ bạn (theo phong cách của dòng CIA) bằng cách làm mịn hình ảnh (làm mờ các cạnh một cách trực quan). Đây là giao diện mặt cười của chúng ta nếu chúng ta phóng to nó mà không làm mịn: Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 5Pixel biến thành hình vuông lớn. Tiếp tục đi. Trong thiết bị đầu cuối, đi tới ~/workspace/pset4/bmp . Chúng tôi nghĩ bạn đã nhớ cách thực hiện việc này. Hãy kiểm tra các byte được phân bổ trong Smiley.bmp . Điều này có thể được thực hiện bằng cách sử dụng trình soạn thảo hex dòng lệnh, chương trình xxd . Để chạy nó, hãy chạy lệnh: xxd -c 24 -g 3 -s 54 smiley.bmp Bạn sẽ thấy những gì được hiển thị bên dưới; Chúng tôi lại đánh dấu màu đỏ tất cả các trường hợp 0000ff. Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 6Trong hình ảnh ở cột ngoài cùng bên trái, bạn có thể thấy các địa chỉ trong tệp, tương đương với phần bù từ byte đầu tiên của tệp. Tất cả chúng đều được đưa ra trong hệ thống số thập lục phân. Nếu chúng ta chuyển đổi hệ thập lục phân 00000036 thành số thập phân, chúng ta sẽ nhận được 54. Vậy bạn đang xem byte thứ 54 từ Smiley.bmp . Hãy nhớ lại rằng trong các tệp BMP 24 bit, 14 + 40 = 54 byte đầu tiên chứa đầy siêu dữ liệu. Vì vậy, nếu bạn muốn xem siêu dữ liệu, hãy chạy lệnh sau: xxd -c 24 -g 3 smiley.bmp Nếu Smiley.bmp chứa các ký tự ASCII , chúng ta sẽ thấy chúng ở cột ngoài cùng bên phải trong xxd thay vì tất cả các dấu chấm đó. Vì vậy, Smiley là BMP 24 bit (mỗi pixel được biểu thị bằng 24 8 = 3 byte) với kích thước (độ phân giải) là 8x8 pixel. Do đó, mỗi dòng (hoặc "Dòng quét" như được gọi) chiếm (8 pixel) x (3 byte mỗi pixel) = 24 byte. Số này là bội số của 4 và điều này rất quan trọng vì tệp BMP được lưu trữ hơi khác nếu số byte trong dòng không phải là bội số của 4. Vì vậy, trong small.bmp, một tệp BMP 24 bit khác trong thư mục của chúng tôi, bạn có thể thấy hộp 3x3 pixel màu xanh lục. Nếu mở nó trong trình xem ảnh, bạn sẽ thấy nó giống với hình ảnh hiển thị bên dưới, chỉ có kích thước nhỏ hơn. Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 7Do đó, mỗi dòng trong Small.bmp chiếm (3 pixel) × (3 byte mỗi pixel) = 9 byte, không phải là bội số của 4. Để có độ dài dòng là bội số của 4, nó được đệm thêm các số 0: trong khoảng từ 0 đến 3 byte, chúng tôi điền vào mỗi dòng ở định dạng BMP 24 bit (bạn có đoán được tại sao lại như vậy không?). Đối với small.bmp , cần có 3 byte số 0, vì (3 pixel) x (3 byte mỗi pixel) + (3 byte phần đệm) = 12 byte, thực sự là bội số của 4. Để "xem" phần đệm này, làm như sau xxd -c 12 -g 3 -s 54 small.bmp Lưu ý rằng chúng tôi sử dụng một giá trị khác cho -c so với Smiley.bmp , vì vậy lần này xxd chỉ xuất ra 4 cột (3 cho hình vuông màu xanh lá cây và 1 cho phần đệm). Để rõ ràng, chúng tôi đã đánh dấu tất cả các trường hợp 00ff00 bằng màu xanh lục. Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 8Ngược lại, hãy sử dụng xxd cho tệp big.bmp . Nó trông giống hệt như Small.bmp, chỉ có độ phân giải của nó là 12x12 pixel, tức là lớn hơn bốn lần. Chạy lệnh dưới đây. Bạn có thể cần mở rộng cửa sổ để tránh chuyển. xxd -c 36 -g 3 -s 54 large.bmp Bạn sẽ thấy một cái gì đó như thế này: Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 9Xin lưu ý, không có nội dung lạc đề nào trong BMP này! Xét cho cùng, (12 pixel) × (3 byte mỗi pixel) = 36 byte và đây là bội số của 4. Trình soạn thảo hex xxd đã hiển thị cho chúng tôi các byte trong tệp BMP của chúng tôi. Làm thế nào chúng ta có thể có được chúng theo chương trình? Trong copy.c có một chương trình có mục đích duy nhất trong đời là tạo ra một bản sao của BMP, từng mảnh một. Có, bạn có thể sử dụng cp cho việc này . Tuy nhiên, cp sẽ không thể giúp được gì cho anh Boddy. Hãy hy vọng copy.c thực hiện điều này, vì vậy chúng ta sẽ bắt đầu: ./copy smiley.bmp copy.bmp Nếu bây giờ bạn chạy ls (với cờ thích hợp), bạn sẽ thấy Smiley.bmpcopy.bmp thực sự có cùng kích thước. Hãy kiểm tra lại xem điều này có thực sự đúng không? diff smiley.bmp copy.bmp Nếu lệnh này không hiển thị bất kỳ thứ gì trên màn hình, điều đó có nghĩa là các tệp thực sự giống hệt nhau (quan trọng: một số chương trình, như Photoshop, bao gồm các số 0 ở cuối một số VMP. Phiên bản sao chép của chúng tôi sẽ loại bỏ chúng, vì vậy đừng lo lắng nếu trong trường hợp sao chép các BMP khác mà bạn đã tải xuống hoặc tạo để thử nghiệm thì bản sao sẽ nhỏ hơn bản gốc vài byte). Bạn có thể mở cả hai tệp trong trình xem ảnh của Ristretto (nhấp đúp) để xác nhận điều này một cách trực quan. Nhưng khác biệt thực hiện việc so sánh này theo từng byte, vì vậy tầm nhìn của cô ấy sắc nét hơn của bạn! Bản sao này được tạo ra như thế nào? Hóa ra copy.c có liên quan đến bmp.h . Hãy đảm bảo: mở bmp.h. Ở đó bạn sẽ thấy định nghĩa thực tế của những tiêu đề mà chúng tôi đã đề cập, được điều chỉnh từ cách triển khai của chính Microsoft. Ngoài ra, tệp này xác định các kiểu dữ liệu BYTE, DWORD, LONG và WORD, là những kiểu dữ liệu thường thấy trong thế giới lập trình Win32 (tức là Windows). Lưu ý rằng về cơ bản đây là các bí danh của các dạng nguyên thủy mà bạn (hy vọng) đã quen thuộc. Hóa ra BITMAPFILEHEADER và BITMAPINFOHEADER đang sử dụng những loại này. Tệp này cũng định nghĩa một cấu trúc có tên RGBTRIPLE. Nó “đóng gói” ba byte: một màu xanh lam, một màu xanh lá cây và một màu đỏ (đây là thứ tự mà chúng ta sẽ tìm kiếm bộ ba RGB trên đĩa). Những cấu trúc này hữu ích như thế nào? Tóm lại, một tệp chỉ đơn giản là một chuỗi byte (hoặc cuối cùng là bit) trên đĩa. Tuy nhiên, những byte này thường được sắp xếp sao cho một số byte đầu tiên đại diện cho một cái gì đó, sau đó một số byte tiếp theo đại diện cho một thứ khác, v.v. "Các định dạng" tệp tồn tại bởi vì chúng tôi có các tiêu chuẩn hoặc quy tắc xác định ý nghĩa của byte. Bây giờ, chúng ta có thể chỉ cần đọc tệp từ đĩa vào RAM dưới dạng một mảng byte lớn. Và chúng ta nhớ rằng byte ở vị trí [i] đại diện cho một thứ, trong khi byte ở vị trí [j] là một thứ khác. Nhưng tại sao không đặt tên cho một số byte này để chúng ta có thể truy xuất chúng từ bộ nhớ dễ dàng hơn? Đây chính xác là những gì cấu trúc trong bmp.h giúp chúng ta. Thay vì coi một tệp là một chuỗi byte dài, chúng ta thấy nó được chia thành các khối dễ hiểu hơn - các chuỗi cấu trúc. Hãy nhớ lại rằng Smiley.bmp có độ phân giải 8x8 pixel, do đó nó chiếm 14 + 40 + (8 × 8) × 3 = 246 byte trên đĩa (bạn có thể kiểm tra điều này bằng lệnh ls). Đây là giao diện trên đĩa theo Microsoft: Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 10Chúng ta có thể thấy thứ tự đó rất quan trọng khi nói đến các thành viên của cấu trúc. Byte 57 là rgbtBlue (không phải rgbtRed) vì rgbtBlue được xác định trong RGBTRIPLE trước tiên. Nhân tiện, việc chúng tôi sử dụng thuộc tính đóng gói đảm bảo rằng clang không cố gắng "căn chỉnh từ" các thành viên (với địa chỉ byte đầu tiên của mỗi thành viên là bội số của 4), để chúng tôi không gặp phải lỗ hổng trong cấu trúc của chúng tôi hoàn toàn không tồn tại trên đĩa. Tiếp tục nào. Tìm các URL khớp với BITMAPFILEHEADER và BITMAPINFOHEADER, theo nhận xét trong bmp.h. Chú ý, khoảnh khắc tuyệt vời: bạn đang bắt đầu sử dụng MSDN (Mạng nhà phát triển Microsoft)! Thay vì cuộn qua copy.c , hãy trả lời một số câu hỏi để hiểu cách hoạt động của mã. Như mọi khi, lệnh man là người bạn thực sự của bạn và bây giờ là MSDN. Nếu bạn không biết câu trả lời, hãy tra Google và suy nghĩ về nó. Bạn cũng có thể tham khảo file stdio.h tại https://reference.cs50.net/.
  1. Đặt điểm dừng trong main (bằng cách nhấp vào bên trái thước có số dòng chính).

  2. Trong tab thiết bị đầu cuối , đi tới ~/workspace/pset4/bmp và biên dịch copy.c vào chương trình sao chép bằng cách sử dụng make.

  3. Chạy debug50 copy Smiley.bmp copy.bmp , thao tác này sẽ mở bảng gỡ lỗi ở bên phải.

  4. Xem qua chương trình từng bước bằng cách sử dụng bảng bên phải. Lưu ý bfbi . Trong ~/workspace/pset4/questions.txt , hãy trả lời các câu hỏi:

  • stdint.h là gì ?

  • Mục đích của việc sử dụng uint8_t , uint32_t , int32_tuint16_t trong một chương trình là gì?

  • BYTE , DWORD , LONGWORD chứa bao nhiêu byte tương ứng (Giả sử kiến ​​trúc 32 bit)?

  • Hai byte đầu tiên của tệp BMP phải là gì (ASCII, thập phân hoặc thập lục phân)? (các byte dẫn đầu, được sử dụng để xác định định dạng tệp (có xác suất cao) thường được gọi là "số ma thuật").
  • Sự khác biệt giữa bfSize và biSize là gì?

  • BiHeight âm có nghĩa là gì?

  • Trường nào trong BITMAPINFOHEADER xác định độ sâu màu trong BMP (tức là số bit trên mỗi pixel)?

  • Tại sao hàm fopen có thể trả về NULL trong copy.c 37?

  • Tại sao đối số thứ ba của fread trong mã của chúng ta lại bằng 1?

  • Giá trị nào trong copy.c 70 xác định phần đệm nếu bi.biWidth là 3?

  • Fseek làm gì?

  • SEEK_CUR là gì?

Trở lại với ông Body. Bài tập:

Viết chương trình có tên whodunit trong một tệp có tên whodunit.c hiển thị bức vẽ của ông Boddy. Hmmmm, cái gì cơ? Giống như copy, chương trình phải lấy chính xác hai đối số dòng lệnh, và nếu bạn chạy chương trình như hình dưới đây thì kết quả sẽ được lưu trong Judge.bmp, trong đó hình vẽ của Mr. Boddy sẽ không bị nhiễu. ./whodunit clue.bmp verdict.b Hãy để chúng tôi gợi ý rằng bạn sẽ bắt đầu giải quyết bí ẩn này bằng cách chạy lệnh bên dưới. cp copy.c whodunit.c Bạn có thể ngạc nhiên về số dòng mã bạn cần viết để giúp ông Boddy. Không có gì không cần thiết ẩn trong Smiley.bmp , vì vậy hãy thoải mái kiểm tra chương trình trên tệp này. Nó nhỏ và bạn có thể so sánh đầu ra của chương trình của mình với đầu ra của xxd trong quá trình phát triển (hoặc có thể có điều gì đó ẩn giấu trong Smiley.bmp ? Thực ra là không). Nhân tiện, vấn đề này có thể được giải quyết theo nhiều cách khác nhau. Một khi bạn xác định được bức vẽ của ông Body, ông ấy sẽ yên nghỉ. Vì whodunit có thể được triển khai theo nhiều cách nên bạn sẽ không thể kiểm tra tính chính xác của việc triển khai bằng check50 . Và hãy để nó làm hỏng cuộc vui của bạn, nhưng giải pháp của trợ lý cũng không có sẵn cho vấn đề whodunit . Cuối cùng, trong tệp Trong ~/workspace/pset4/questions.txt , hãy trả lời câu hỏi sau: Whodunit? //ктоэтосделал?

thay đổi kích thước

Chà, bây giờ - bài kiểm tra tiếp theo! Hãy viết một chương trình có tên là thay đổi kích thước trong thay đổi kích thước.c . Nó sẽ thay đổi kích thước hình ảnh BMP 24-bit không nén theo các bước n. Ứng dụng của bạn phải chấp nhận chính xác ba đối số dòng lệnh, với (n) đầu tiên là số nguyên không lớn hơn 100, đối số thứ hai là tên của tệp sẽ được sửa đổi và đối số thứ ba là tên của phiên bản đã lưu của tệp đã sửa đổi. tài liệu. Usage: ./resize n infile outfile Với chương trình như vậy, chúng ta có thể tạo big.bmp từ Small.bmp bằng cách thay đổi kích thước của Small.bmp bằng 4 (tức là nhân cả chiều rộng và chiều cao với 4), như hiển thị bên dưới. ./resize 4 small.bmp large.bmp Để đơn giản, bạn có thể bắt đầu tác vụ bằng cách sao chép lại copy.c và đặt tên cho bản sao thay đổi kích thước.c . Nhưng trước tiên, hãy tự hỏi bản thân và trả lời những câu hỏi sau: việc thay đổi kích thước BMP có ý nghĩa gì (bạn có thể giả định rằng n lần kích thước tệp trong sẽ không vượt quá 232 - 1) . Xác định trường nào trong BITMAPFILEHEADER và BITMAPINFOHEADER bạn cần thay đổi. Xem xét xem bạn có cần thêm hoặc xóa các trường dòng quét hay không . Và, vâng, hãy biết ơn vì chúng tôi không yêu cầu bạn xem xét tất cả các giá trị có thể có của n từ 0 đến 1! (mặc dù, nếu bạn quan tâm thì đây là một vấn đề trong sách của hacker ;)). Tuy nhiên, chúng tôi giả định rằng với n = 1, chương trình sẽ hoạt động chính xác và tệp outfile đầu ra sẽ có cùng kích thước với tệp infile gốc. Bạn có muốn kiểm tra chương trình bằng check50 không? Nhập lệnh sau: check50 2015.fall.pset4.resize bmp.h thay đổi kích thước.c Bạn có muốn thử nghiệm việc triển khai ứng dụng do trợ lý CS50 thực hiện không? Chạy lệnh sau: ~cs50/pset4/resize Chà, nếu bạn muốn xem, ví dụ: các tiêu đề big.bmp (ở dạng thân thiện với người dùng hơn xxd cho phép), bạn cần chạy lệnh sau: ~cs50/pset4/peek large.bmp Thậm chí tốt hơn, nếu bạn muốn so sánh các tiêu đề có tiêu đề của tệp trợ lý CS50. Bạn có thể chạy các lệnh bên trong thư mục ~/workspace/pset4/bmp của mình (hãy nghĩ xem mỗi lệnh làm gì). Nếu bạn đã sử dụng malloc , hãy nhớ sử dụng free để tránh rò rỉ bộ nhớ. Hãy thử sử dụng valgrind để kiểm tra rò rỉ. ./resize 4 small.bmp student.bmp
~cs50/pset4/resize 4 small.bmp staff.bmp
~cs50/pset4/peek student.bmp staff.bmp

Làm thế nào để quyết định?

  • Mở tệp mà chúng ta cần phóng to, đồng thời tạo và mở một tệp mới trong đó hình ảnh phóng to sẽ được ghi lại;

  • cập nhật thông tin tiêu đề cho tệp đầu ra. Vì hình ảnh của chúng tôi ở định dạng BMP và chúng tôi đang thay đổi kích thước của nó nên chúng tôi cần viết tiêu đề của tệp mới với kích thước mới. Điều gì sẽ thay đổi? Kích thước tệp cũng như kích thước hình ảnh - chiều rộng và chiều cao của nó.

Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 11Nếu nhìn vào mô tả tiêu đề, chúng ta sẽ thấy biến biSizeImage . Nó cho biết tổng kích thước của hình ảnh tính bằng byte, biWidth là chiều rộng của hình ảnh trừ đi căn chỉnh, biHeight là chiều cao. Các biến này nằm trong cấu trúc BITMAPFILEHEADER và BITMAPINFOHEADER. Bạn có thể tìm thấy chúng nếu mở tệp bmp.h. Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 12Mô tả cấu trúc BITMAPINFOHEADER chứa danh sách các biến. Để viết tiêu đề của tệp đầu ra, bạn sẽ cần thay đổi giá trị chiều cao và chiều rộng. Nhưng cũng có khả năng sau này bạn sẽ cần chiều cao và chiều rộng ban đầu của tệp gốc. Vì vậy, tốt hơn là giữ cả hai. Hãy cẩn thận với tên biến để không vô tình ghi sai dữ liệu vào tiêu đề của tệp đầu ra.
  • Chúng tôi đọc tệp gửi đi, từng dòng, từng pixel. Để thực hiện việc này, chúng ta lại quay lại thư viện I/O tệp và hàm fread. Nó đưa một con trỏ tới một cấu trúc sẽ chứa các byte đã đọc, kích thước của phần tử đơn lẻ mà chúng ta sẽ đọc, số lượng các phần tử như vậy và một con trỏ tới tệp mà chúng ta sẽ đọc từ đó.

    Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 13
  • Chúng ta tăng từng dòng theo chiều ngang theo tỷ lệ đã chỉ định và ghi kết quả vào tệp đầu ra.

    Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 14

    Chúng ta ghi tập tin như thế nào? Chúng ta có một hàm fwrite, trong đó chúng ta chuyển một chỉ báo tới cấu trúc nơi chứa dữ liệu được ghi vào tệp, kích thước của phần tử, số của chúng và một con trỏ tới tệp đầu ra. Để tổ chức vòng lặp, chúng ta có thể sử dụng vòng lặp for mà chúng ta đã quen thuộc .

    Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 15
  • Điền vào chỗ trống! Nếu số pixel trên một dòng không phải là bội số của bốn, chúng ta phải thêm "căn chỉnh" - 0 byte. Chúng ta sẽ cần một công thức để tính kích thước căn chỉnh. Để ghi byte rỗng vào tệp đầu ra, bạn có thể sử dụng hàm fputc, truyền cho nó ký tự bạn muốn ghi và một con trỏ tới tệp đầu ra.

    Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 16

    Bây giờ chúng ta đã kéo dài chuỗi theo chiều ngang và thêm căn chỉnh vào tệp đầu ra, chúng ta cần di chuyển vị trí hiện tại trong tệp đầu ra vì chúng ta cần chuyển qua căn chỉnh.

    Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 17
  • Tăng kích thước dọc. Nó phức tạp hơn, nhưng chúng ta có thể sử dụng mã mẫu từ copy.c (copy.c mở tệp đầu ra, ghi tiêu đề vào tệp đầu ra, đọc hình ảnh từ tệp nguồn theo từng dòng, từng pixel và ghi chúng vào tập tin đầu ra). Dựa trên điều này, điều đầu tiên bạn có thể làm là chạy lệnh sau: cp copy.c thay đổi kích thước.c

    Kéo giãn hình ảnh theo chiều dọc có nghĩa là sao chép từng dòng nhiều lần. Có một số cách khác nhau để làm điều này. Ví dụ: bằng cách sử dụng tính năng viết lại, khi chúng ta lưu tất cả pixel của một dòng vào bộ nhớ và ghi dòng này vào tệp đầu ra trong một vòng lặp nhiều lần nếu cần. Một phương pháp khác là sao chép lại: sau khi đọc một dòng từ tệp gửi đi, ghi nó vào tệp đầu ra và căn chỉnh nó, đưa hàm fseek trở lại đầu dòng trong tệp gửi đi và lặp lại mọi thứ một vài lần.

    Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 18
  • hồi phục

    Để chuẩn bị cho bài tập của Tuần 4, tôi đã dành vài ngày qua để xem các bức ảnh được lưu ở định dạng JPEG bằng máy ảnh kỹ thuật số của tôi trên thẻ nhớ CompactFlash (CF) 1 GB. Làm ơn đừng nói với tôi rằng tôi thực sự đã dành vài ngày qua trên Facebook. Thật không may, kỹ năng máy tính của tôi còn nhiều điều chưa được mong đợi và tôi đã vô tình xóa hết ảnh mà không hề hay biết! May mắn thay, trong thế giới máy tính, “xóa” thường không có nghĩa là “bị giết”. Máy tính của tôi báo rằng thẻ nhớ hiện đã trống nhưng tôi biết nó đang nói dối. Bài tập: Viết chương trình trong ~/workspace/pset4/jpg/recover.c để khôi phục những bức ảnh này. Hừm. Được rồi, đây là một điều làm rõ hơn. Mặc dù định dạng JPEG phức tạp hơn BMP nhưng JPEG có "chữ ký", các mẫu byte giúp phân biệt nó với các định dạng tệp khác. Hầu hết các tệp JPEG đều bắt đầu bằng ba byte sau: 0xff 0xd8 0xff Từ byte đầu tiên đến byte thứ ba, từ trái sang phải. Byte thứ tư rất có thể sẽ là một trong các kết hợp sau: 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,0xe8, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef. Nói cách khác, bốn bit đầu tiên của byte thứ tư của tệp JPEG là 1110. Rất có thể, nếu bạn tìm thấy một trong những mẫu này trên ổ đĩa nơi lưu trữ ảnh (như thẻ nhớ của tôi), thì đây sẽ là phần khởi đầu của tệp JPEG. Tất nhiên, bạn có thể gặp phải trường hợp này trên đĩa nào đó hoàn toàn là tình cờ, phục hồi dữ liệu không thể gọi là một môn khoa học chính xác.

    Làm thế nào để quyết định

    1. Mở tập tin có nội dung trong thẻ nhớ.

    2. Tìm phần đầu của tệp JPEG. Tất cả các tập tin trên thẻ này đều là hình ảnh ở định dạng JPEG.

    Bạn đã biết về điểm đánh dấu JPEG: Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 19Điều quan trọng (và thú vị) là biết rằng mỗi tệp JPEG được lưu trữ trong bộ nhớ dưới dạng một khối duy nhất và các tệp này nối tiếp nhau. Hãy vẽ một sơ đồ bộ nhớ. Mỗi hình chữ nhật là một khối dài 512 byte. Hình chữ nhật màu xám là vùng không có tệp JPEG; dấu hoa thị cho biết phần đầu của tệp JPEG. Chúng tôi tin rằng chúng tôi không có khối màu xám giữa các tệp. Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 20Làm cách nào để chúng tôi đọc dữ liệu này, 512 byte này, để so sánh phần đầu của nó với tiêu đề JPEG? Chúng ta có thể sử dụng hàm fread , hàm này đã quen thuộc với chúng ta, hàm này đưa con trỏ tới cấu trúc dữ liệu nơi các byte đọc sẽ được ghi, cũng như kích thước của phần tử đang được đọc, số lượng phần tử đó và một con trỏ tới tệp mà chúng ta đang đọc dữ liệu. Harvard CS50: Bài tập tuần 4 (Bài 9 và 10) - 21Chúng tôi muốn đọc 512 byte và lưu trữ chúng trong bộ đệm. Đây sẽ là con trỏ &data và con trỏ inptr sẽ trỏ đến tệp đang mở có nội dung của thẻ nhớ. Vì vậy, hãy quay lại cấu trúc tệp của chúng tôi, trong đó chúng tôi lưu
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION