binaryoffset Ответов: 4

Запись растрового изображения в новый файл изображения C++


Всем привет. В настоящее время я пытаюсь скопировать заголовок и пиксельные данные из файла изображения и записать их в новый файл изображения. Я использую 256-цветное растровое изображение и для тестирования выбрал фиксированную высоту и ширину пикселя 150 и 90. Создается новый файл изображения файла, но при попытке загрузить его выдается следующее сообщение: "Paint не может прочитать этот файл. Это недопустимый растровый файл или его формат в настоящее время не поддерживается. Буду признателен за поддержку после многих попыток. Ниже приведен код, который я использую:

02     const int height = 150;  

03     const int width = 90;  

05     unsigned char m_cHeaderData[1078];  

06     unsigned char** m_cImageData = new unsigned char* [height];  
  

08     for( int i = 0; i <height; i++)  

09     {  

10         m_cImageData[i] = new unsigned char [width];  

11     }  

12    

13     ifstream* m_pInFile;      

14     m_pInFile = new ifstream;  

15     m_pInFile->open("image.bmp", ios::in | ios::binary);  

16     m_pInFile->seekg(0, ios::beg);  

17     m_pInFile->read(reinterpret_cast<char*>(m_cHeaderData), 1078); 
       //bitmap bits start at offset 1078  

18    

19     for(int i = 0; i <height; i++)  

20     {  

21         m_pInFile->read(reinterpret_cast<char*>(m_cImageData[i]), width);   

22     }  

23    

24     m_pInFile->close();  

25    

26     ofstream* m_pOutFile;   

27     m_pOutFile = new ofstream;  

28     m_pOutFile->open("imageCopy.bmp", ios::out | ios::trunc | ios::binary);      

29     m_pOutFile->write(reinterpret_cast<char*>(m_cHeaderData), 1078);  

30     for(int i = 0; i <height; i++)      

31     {  

32         m_pOutFile->write(reinterpret_cast<char*>(m_cImageData[i]), width;  

33     }  

34     m_pOutFile->close();  


37     //deallocate memory:  

38     delete m_pInFile;  

39     delete m_pOutFile;  

40     for(int i = 0; i <height; i++)  

41     {  

42         delete[] m_cImageData[i];  

43     }  

44     delete[] m_cImageData;  

4 Ответов

Рейтинг:
2

Jochen Arndt

Вы должны взглянуть на формат растрового файла (Википедия[^]).

Вы не должны использовать фиксированный размер заголовка и фиксированные свойства. Вместо этого считайте данные из заголовка файла.

Даже если ваш растровый файл имеет используемые свойства (размер заголовка 1078, 256-битные цвета, соответствующие ширина и высота), вы не создадите правильный растровый файл. Это происходит потому, что данные упаковываются в строки, где размер каждой строки должен быть кратен 4. при ширине 90 пикселей каждая строка хранится в 92 байтах, а не в 90.

[ОБНОВЛЕНИЕ]
Вы можете поискать в интернете пример кода о том, как считывать растровый файл в память. Примеры можно найти здесь: http://www.cplusplus.com/articles/GwvU7k9E/[^] и здесь: https://graphics.stanford.edu/~mdfisher/Code/Engine/Bitmap.cpp.html[^].

То, какие данные должны быть прочитаны, зависит от того, что вы хотите сделать. Если вы хотите записать изображение обратно в новый файл, вы должны прочитать и проанализировать необходимую информацию заголовка и биты изображения. Поскольку существуют разные версии заголовков (с разными размерами) и необязательные таблицы цветов, вы должны прочитать их все, проанализировать информацию и записать все данные в новый файл в том же порядке.

Обратите также внимание, что более эффективно считывать биты изображения в один большой буфер, а не выделять буферы для каждой строки. Затем этот буфер включает в себя шаг (байты заполнения для строк).


CPallini

5.

binaryoffset

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

Исходное изображение имеет размер 14,5 к.
А скопированное изображение (которое теперь открывается) имеет размер 14,7 к.

Я уже использовал BITMAPFILEHEADER и BITMAPINFOHEADER для извлечения ширины и высоты изображения перед попыткой чтения и записи, но я все еще сталкивался с той же проблемой (что скопированный файл не открывался). Я даже использовал значение смещения от bfOffBits.

Как бы я правильно "прочитал" из заголовочного файла?

Если ширина окажется не кратной 4, должен ли я изменить значение в программе на ближайшее кратное 4?

Заранее спасибо за ваши отзывы.

Jochen Arndt

Смотрите мой обновленный ответ. Но было бы хорошо знать, что Вы, наконец, хотите сделать. В зависимости от этого некоторые шаги синтаксического анализа заголовка могут быть опущены.

binaryoffset

Спасибо за Ваш отзыв. В конце концов я хочу перейти к применению алгоритмов обработки изображений (например, RGB в оттенках серого). Первоначальная идея состояла в том, чтобы извлечь пиксельные данные в массив (1D или 2D), а затем перейти к извлечению цветов RGB из каждого пикселя.

Рейтинг:
2

binaryoffset

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

Исходное изображение имеет размер 14,5 к.
А скопированное изображение (которое теперь открывается) имеет размер 14,7 к.

Я уже использовал BITMAPFILEHEADER и BITMAPINFOHEADER для извлечения ширины и высоты изображения перед попыткой чтения и записи, но я все еще сталкивался с той же проблемой (что скопированный файл не открывался). Я даже использовал значение смещения от bfOffBits.

Как бы я правильно "прочитал" из заголовочного файла?

Если ширина окажется не кратной 4, должен ли я изменить значение в программе на ближайшее кратное 4?

Заранее спасибо за ваши отзывы.


Рейтинг:
1

CPallini

Как указывалось выше Йохен каждая горизонтальная строка исходного файла содержит 2 запасные байты (так как шаг растрового изображения должен быть кратен 4). Следовательно, ваш "файл копии" на самом деле 300 байт меньше, чем исходный.


Рейтинг:
0

Member 13855312

void CMRSMATHDlg::Loadit(TCHAR *destination)
{
	CImage img;
	bool reverse=false;
	BITMAPINFOHEADER Info;
	BITMAPFILEHEADER bFileHeader;	
	CFile file2;
	file2.Open(destination, CFile::modeRead | CFile::typeBinary);
	file2.Read(&bFileHeader, sizeof(BITMAPFILEHEADER));
	file2.Read(&Info, sizeof(BITMAPINFOHEADER));
	BYTE ch;
	int width = Info.biWidth;
	int height = Info.biHeight;
	if (load1.m_hObject!=NULL)
	 {
	 load1.Detach();
	 //load1.DeleteObject();
	 //load1.CreateCompatibleBitmap(GetDC(),width,height);
	 }
	if (height > 0)
	{
		reverse = true;
	}
	if (height<0)
		height = -height;
	int size1 = width*height * 3;
	int size2 = ((width * 24 + 31) / 32) * 4 * height;
	int gap = (size2 - size1) / height;			
	BYTE * buffer1 = (BYTE *)GlobalAlloc(GPTR, size2);		
	//////////////////////////
	HGDIOBJ old;
	unsigned char alpha = 0;	
	int z = 0;	
	z = 0;			
	CRect rc;
	GetClientRect(&rc);					
	memset(buffer1,255,size2);	
	file2.Read(buffer1,size2);		
     int dpi=GetDC()->GetDeviceCaps(LOGPIXELSX);
	 int dpmm=dpi/25; //dots per mm<br/>
	 BITMAPINFO *info1;
	 info1=(BITMAPINFO *)new  BYTE[sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256]; 	 
	 info1->bmiHeader=Info;
	 info1->bmiHeader.biXPelsPerMeter=dpmm*1000;
	 info1->bmiHeader.biYPelsPerMeter=dpmm*1000;
	 info1->bmiHeader.biBitCount=24;	 
	 RGBQUAD *rgbPalette=(RGBQUAD *)malloc(sizeof(RGBQUAD)*256); 
   for(int i=0; i<256; i++){
     rgbPalette[i].rgbRed = (byte)i;
     rgbPalette[i].rgbGreen = (byte)i;
     rgbPalette[i].rgbBlue = (byte)
     rgbPalette[i].rgbReserved = 0;}
     memcpy(info1->bmiColors, rgbPalette, sizeof(RGBQUAD) * 256);
	 //info1.bmiColors=	
	 CDC mem;
	 mem.CreateCompatibleDC(0);
	 HDC hDC = mem.m_hDC;
	 HDC m_hDC = CreateCompatibleDC(hDC);	 	 
	 load1.Attach(CreateDIBSection(NULL,info1,DIB_RGB_COLORS, (LPVOID*)0,0,0));	 	 	 
	 load1.SetBitmapBits(size2,buffer1);
	 load1width=width;
	 load1height=height;
	 CImage s1;	 
	 GlobalFree(buffer1);	 
	 file2.Close();
 }

void CMRSMATHDlg::saveit(CBitmap &bit1, CDC &memdc, TCHAR *destination,bool isreverse)
{
	 BITMAP bm;
	 BITMAPINFOHEADER Info;
	 BITMAPFILEHEADER bFileHeader;
	 CFile file1;
     CSize size = bit1.GetBitmap(&bm);
	 int z = 0;
	 BYTE ch = 0;
	 size.cx = bm.bmWidth;
	 size.cy = bm.bmHeight;
	 int size1 = (size.cx)*(size.cy);
	 int size2 = size1 * 3;
	 size1 = (((size.cx) * 24 + 31) / 32) *4* (size.cy);
	 int gap = (size1 - size2) / (size.cy);
	 BYTE * buffer = (BYTE *)GlobalAlloc(GPTR, size2);
	 BYTE * buffer1 = (BYTE *)GlobalAlloc(GPTR, size.cx*3);
	 BYTE * gbuffer = (BYTE *)GlobalAlloc(GPTR, gap);
	 memset(gbuffer, 0, gap);
	 bFileHeader.bfType = 'B' + ('M' << 8);
	 bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
	 bFileHeader.bfSize = bFileHeader.bfOffBits + size1;
	 bFileHeader.bfReserved1 = 0;
	 bFileHeader.bfReserved2 = 0;
	 Info.biSize = sizeof(BITMAPINFOHEADER);
	 Info.biPlanes = 1;
	 Info.biBitCount = 24;//bm.bmBitsPixel;//bitsperpixel///////////////////32
	 Info.biCompression = BI_RGB;
	 Info.biWidth =size.cx;
	 Info.biHeight =size.cy;///reverse pic if negative height
	 Info.biSizeImage =size1;
	 Info.biClrImportant = 0;	 
	 Info.biClrUsed = 0;
	 Info.biXPelsPerMeter = 0;
	 Info.biYPelsPerMeter = 0;
	 bit1.GetBitmapBits(size2, buffer);	 	 
	 file1.Open(destination, CFile::modeCreate | CFile::modeWrite |CFile::typeBinary, 0);
	 file1.Write(&bFileHeader, sizeof(BITMAPFILEHEADER));
	 file1.Write(&Info, sizeof(BITMAPINFOHEADER));
	 unsigned char alpha = 0;
	 int i;
	 for (int y = 0;y<size.cy;y++)
	 {
		 i=0;
		 for (int x = 0;x<size.cx;x++)
		 {
		  //for reverse picture below
	      //z = (((size.cy - 1 - y)*size.cx) + (x)) * 3;
			if (isreverse)
		  z = (((size.cy-1-y)*(size.cx)) + (x)) * 3;
			else
			z=(y*size.cx+x)*3;
		  buffer1[i]=buffer[z];
		  buffer1[i+1]=buffer[z+1];
		  buffer1[i+2]=buffer[z+2];
		  i+=3;
		 }
		  file1.Write(buffer1,size.cx*3);
		  file1.Write(&gbuffer, gap);
	 }
	 GlobalFree(buffer);
	 GlobalFree(gbuffer);
	 GlobalFree(buffer1);
	 file1.Close();
	 file1.m_hFile = NULL;
 }