undefined

Задание №2: resize

Harvard CS50
4 уровень , 6 лекция
Открыта

Ну а теперь — следующее испытание! Давайте напишем в файле resize.c программу, называемую resize. Она будет менять размеры несжатого 24-битного изображения BMP с шагом в n. Ваше приложение должно принимать ровно три аргумента командной строки, причем первый (n) должен быть целым числом не более 100, второй — именем файла, который будет изменен, а третий — названием сохраненной версии измененного файла.

<codeUsage: ./resize n infile outfile>

С помощью такой программы мы могли бы создать large.bmp из файла small.bmp путем изменения размера последнего на 4 (т.е. путем умножения и ширины, и высоты на 4), как показано ниже.

./resize 4 small.bmp large.bmp

Для простоты можно начать задание скопировав еще раз copy.c и назвав копию resize.c.

Но сначала задайте себе вопросы и ответьте на них: что значит изменить размер BMP (можно предположить, что n помноженное на размер файла infile не будет превышать 232 — 1).

Определите, какие поля в BITMAPFILEHEADER и BITMAPINFOHEADER вам нужно изменить.

Подумайте, нужно ли вам добавить или удалить поля scanlines.

И, да, скажите спасибо, что мы не предлагаем вам рассмотреть все возможные значения n от 0 до 1! (хотя, если интересно — это задачка из хакерского задачника;)). Тем не менее, мы предполагаем, что при n = 1 программа будет работать правильно, и выходной файл outfile будет таких же размеров как и исходный infile.

Хотите проверить программку с помощью check50? Наберите следующую команду:

check50 2015.fall.pset4.resize bmp.h resize.c

Есть желание поиграться с реализацией приложения, выполненной ассистентами CS50? Выполните следующее:

~cs50/pset4/resize

Ну а если хотите посмотреть, например, заголовки large.bmp (в более дружественном для пользователя виде, чем позволяет xxd), нужно выполнить следующую команду:

~cs50/pset4/peek large.bmp

Еще лучше, если вы захотите сравнить свои заголовки с заголовками файлов ассистентов CS50. Вы можете выполнить команды внутри вашей директории ~/workspace/pset4/bmp (подумайте, что делает каждая команда).

./resize 4 small.bmp student.bmp
~cs50/pset4/resize 4 small.bmp staff.bmp
~cs50/pset4/peek student.bmp staff.bmp

Если вы использовали malloc, не забудьте использовать free, чтобы предотвратить утечку памяти. Попробуйте использовать valgrind чтобы проверить наличие утечек.

Как решать?

  • Открыть файл, который нам нужно увеличить, а также создать и открыть новый файл, в котором будет записано увеличенное изображение.
  • Обновить информацию заголовка для выходного файла. Поскольку наше изображение в формате BMP и мы меняем его размер, нам нужно записать заголовок нового файла с новыми размерами. Что поменяется? Размер файла, а также размер изображения — его ширина и высота.
  • Задание №2: resize - 1

    Если мы заглянем в описание заголовка, то увидите переменную biSizeImage. Она указывает на общий размер изображения в байтах, biWidth — ширина изображения минус выравнивание, biHeight — высота. Эти переменные находятся в структурах BITMAPFILEHEADER и BITMAPINFOHEADER. Вы можете найти их, если откроете файл bmp.h.

    Задание №2: resize - 2

    В описании структуры BITMAPINFOHEADER есть список переменных. Для записи заголовка выходного файла нужно будет поменять значение высоты и ширины. Но есть также вероятность, что позднее вам понадобятся и оригинальные высота и ширина исходного файла. Поэтому лучше сохранить и то, и другое. Будьте внимательны к названиям переменных, чтобы случайно не записать ошибочные данные в заголовок выходного файла.

    • Читаем исходящий файл, строка за строкой, пиксель за пикселем. Для этого мы снова обращаемся к нашей библиотеке файлового ввода/вывода и функции fread. Она принимает указатель на структуру, которая будет содержать прочитанные байты, размер одного элемента, который мы собираемся прочитать, количество таких элементов и указатель на файл, из которого мы будем читать.
    • Задание №2: resize - 3
    • Увеличиваем каждую строку по горизонтали согласно заданному масштабу, записываем результат в выходной файл
    • Задание №2: resize - 4

      Как мы записываем файлы? У нас есть функция fwrite, которой мы передаем указатель на структуру (там находятся данные для записи в файл), размер элемента, их количество и указатель на выходной файл. Для организации цикла можем воспользоваться уже привычным нам циклом for.

      Задание №2: resize - 5
    • Заполнить пробелы! Если количество пикселей в строке не кратно четырем, мы должны добавить «выравнивание» – нулевые байты. Нам понадобится формула для расчета размера выравнивания. Для записи нулевых байтов в выходной файл вы можете использовать функцию fputc, передав ей символ, который вы хотите записать и указатель на выходной файл.
    • Задание №2: resize - 6
    • Теперь, когда мы растянули строку по горизонтали и добавили выравнивание в выходной файл, нам нужно переместить текущую позицию в исходящем файле, поскольку нам нужно перескочить через выравнивание.
    • Задание №2: resize - 7
    • Увеличить размер по вертикали. Это сложнее, но мы можем использовать пример кода из файла copy.c(copy.c открывает исходящий файл, записывает заголовок в выходной файл, читает изображение из исходного файла по строкам, пиксель за пикселем, и записывает их в файл выхода). Исходя из этого, первым делом можно выполнить такую команду: cp copy.c resize.c

    Растянуть изображение по вертикали означает скопировать каждую строку несколько раз. Есть несколько разных способов это сделать. Например, с помощью перезаписи, когда мы сохраняем все пиксели одной строки в памяти и в цикле записываем эту строку в выходной файл столько раз, сколько это нужно. Другой метод – перекопирование: после чтения строки исходящего файла, записи его в выходной файл и выравнивания вернуться функцией fseek назад в начало строки в исходящем файле и повторить все заново несколько раз.

    Задание №2: resize - 8
Комментарии (12)
Чтобы просмотреть все комментарии или оставить свой,
перейдите в полную версию
Ярослав 0 уровень, Киев
10 сентября 2020
Сначала опечатки и неточности, потом ход решения: 1 - 232 это опечатка!!! You may assume that f times the size of infile will not exceed 2^32 - 1. original http 2 - Неточность, которая запутывает: "Если мы заглянем в описание заголовка, то увидите переменную biSizeImage. Она указывает на общий размер изображения в байтах" Вообще bfSize; это общий размер. А biSize - это размер второй структуры Bmp файла BITMAPINFOHEADER. Да там написано biSizeImage, но лучше называть вещи правильно bfSize Решение: Вся сложность программы в том, что бы понять что в циклах надо ставить границы с входящего файла. + отступы надо контролировать! Пропускать для входящего и добавлять для нового. Отступов теперь 2 переменные. abs используем для biWidth! что бы число было с +. Ну и простейший счетчик внутри каждого цикла для n-раз. j2 = j2 - 1; if (j2 == 0) // если правда - новый круг цикла / новый пиксель { j = j + 1; j2 = n; } else fseek(inptr, - sizeof(triple), SEEK_CUR); // возвращаемся назад на пиксель.
Михаил 20 уровень, Санкт-Петербург
10 февраля 2019
Может кто объяснить, как вычислить фактический размер. А если точнее как заполнение вычислить сколько будет? При 3х кратном увеличении умножаем размер на 3 + RGB triple * на заполнение?
Alexander Levin 4 уровень, Москва
14 октября 2018
Главное следить за курсором..
Roman Andreev 1 уровень
18 октября 2017
Интересно, кто нибудь делал хакерскую версию, где изображение не только увеличивается, но еще и сжимается? Понятно, что если мы , например увеличили изображение сначала в N раз, то обратными действиями его можно на столько же уменьшить. Но как быть, если нам, скажем изображение 3х3 пикселя нужно уменьшить в 5 раз? Пока даже понять не могу по какому принципу это реализуется. Пиксель это же цельная величина, как мы можем отрисовать 0,6 пикселя, да еще и сохранив рисунок при этом?
George Tushinsky 4 уровень
30 августа 2017
check50 cs50/2017/x/resize/less, теперь так надо писать. (http://docs.cs50.net/problems/resize/less/resize.html)
Fonzy 40 уровень, Москва
29 августа 2017
check50 2015.fall.pset4.resize bmp.h resize.c - у всех не работает или это я что-то не так делаю?