В настоящее время я делаю свое первое программирование на C ++, и моя цель — масштабировать растровое изображение, используя только основные заголовки ввода-вывода.
Я использовал http://en.wikipedia.org/wiki/BMP_file_format#DIB_header_.28bitmap_information_header.29 как мой справочник по поиску информации заголовка.
Моя проблема заключается в создании алгоритма для увеличения изображения с точки зрения добавления отдельных пикселей.
Я сделал серию циклов for, которые комментируются (они используют информацию заголовка, чтобы определить, сколько нужно запустить), но я понятия не имею, что делать дальше с точки зрения манипулирования фактическими пикселями, это где мне нужна помощь
Когда я запускаю программу, создается новый файл BMP (называемый Image2), однако он поврежден и имеет размер 1 КБ (скорее всего потому, что программа не завершена).
Вот код, если кто-то может также критиковать меня по этому поводу или сообщить мне о любых плохих практиках, чтобы избежать того, что я делаю в настоящее время, я был бы признателен.
int main()
{
FILE* pFile = NULL;
FILE* pFile2 = NULL;//Open Image.bmp (In directory) as read binary
pFile = fopen("Image.bmp, "rb");
//Scan through the program and set to send.
fseek (pFile, 0, SEEK_END);
//Read file data
int fileData = ftell (pFile);
//set back to start of char pointer
rewind (pFile);//Create a Char Filebuffer, this will be where data is viewed.
char* FileBuffer = new char[fileData];
//Read pFile into Filebuffer
fread(FileBuffer, fileData, 1, pFile);
//Read out BMP Header Data and Cast to Int
int ImageSize = *(int*)&FileBuffer[2];
int ImageOffset = *(int*)&FileBuffer[10]; //ImageOffset = Image Header Size
int ImageHeight = *(int*)&FileBuffer[18];
int ImageWidth = *(int*)&FileBuffer[20];
int BitsPerPixel = *(int*)&FileBuffer[24];
//Create a New Buffer that starts off at Pixel Data
char* NewFileBuffer = FileBuffer + ImageOffset;std::cout << "Enter the amount of times you want to scale the image by." << std::endl;
std::cin >> ScaleValue;
std::cout << "\n";
//Create New Image row/height/size variables
int RowSizeOld = (BitsPerPixel * ImageWidth +31)/32 * 4;
int RowSizeNew = (BitsPerPixel *(ImageWidth * ScaleValue) + 31) / 32 * 4;
int NewImageSize = ImageOffset + (RowSizeNew * (ScaleValue * ImageHeight) );
int NewImageHeight = ImageHeight * ScaleValue;
int NewImageWidth = ImageWidth * ScaleValue;// These ints define the colour values for RGB and Padding
int r = ImageOffset + 1;
int g = ImageOffset + 2;
int b= ImageOffset + 3;
int p = ImageOffset + 4;//Rescale Image here, for (Newfile buffer [0])
// This for loop figures out how many times the newHeight variable must iterate
for (int newHeight = 0 ; newHeight < NewImageHeight ; newHeight++)
{
//This line scales up the amount of rows in terms of scale value
for(int doubleLine = 0 ; doubleLine < ScaleValue ; doubleLine++)
{
//This for loop then figures out the width of the new image in pixels as an int value
for (int newWidth = 0 ; newWidth < NewImageWidth ; newWidth++)
{
//This loop figures out how many times you need to increase the pixel density (for each pixel)
for (int pixelMultiplier = 0 ; pixelMultiplier < ScaleValue ; pixelMultiplier++)
{
//Move pixel data around to scale image (This is where I'm having trouble)
}
}}}//Create a new File pointer, add image name + bmp string
FILE* pFile2;
pFile2 = fopen("Image2.bmp", "wb");
fwrite(NewFileBuffer, (NewImageSize + ImageOffset), sizeof(char) , pFile2);
return 0;
}
Это мой первый пост на SE, поэтому я приношу свои извинения, если формат кода немного коренастый или вопрос сложный для понимания (я прочитал форум и урезал свой код, чтобы сделать его более читабельным)
Выполнение простого ближайшего повторного сэмплирования, вероятно, является самым простым способом сделать это. Для этого вы просто рассчитываете соотношение между новым и старым размерами следующим образом:
float xStep = (float)oldWidth / (float)newWidth;
float yStep = (float)oldHeight / (float)newHeight;
Теперь вы знаете, что, когда вы шагаете на 1 пиксель в направлении x для нового изображения, вы добавляете пиксели «xStep» к старому изображению. Затем вы округляете позицию сэмплирования до ближайшего целочисленного местоположения, выбираете ее из старого изображения и записываете в новое! Теперь у вас есть простое изображение с повторной выборкой.
Для еще лучших результатов вы можете Билинейный фильтр как вы повторно образца. Это очень простая 2D линейная интерполяция.
Чтобы дать вам одномерный пример фильтрации значений с плавающей запятой, вы должны написать некоторый код, подобный следующему:
for( float x = 0, int xOut = 0; x += xStep, xOut++; xOut < newWidth; )
{
const float sample0 = old1D[std::floor( x )];
const float sample1 = old1D[std::ceil( x )];
const float t = x - std::floor( x );
const float sample = sample0 + ((sample1 - sample0) * t);
new1D[xOut] = sample;
}
Конечно, я не забочусь о крайних случаях, но заботу о них и расширение до 2D следует оставить в качестве упражнения в случае назначения.
Также стоит отметить, что этот вид фильтрации будет выглядеть немного неправильно, если вы масштабируете изображение более чем в 2 раза из-за того, что вы по-прежнему полностью пересекаете пиксели. Это обычно решается путем предварительной фильтрации изображения в набор изображений половинной ширины и высоты на каждом шаге. Это известно как MIP-отображение.
Удачи!
Идея состоит в том, чтобы умножить существующие пиксели, а не добавлять полностью новые.
Для положительного масштабирования вы можете создать новое растровое изображение, в x раз больше старого.
Затем заполните новое растровое изображение старыми пикселями и дубликатами.
Схема выглядит следующим образом:
| O1 | d1 | o2 | d2 |…
| D1 | d1 | d2 | d2 |…
Где o1 — старый пиксель, а d1 — его дубликат.
Таким образом, вам придется просмотреть все пиксели и определить, как часто и где они будут заполнены в новом растровом изображении.