BMP 8bit odczyt/zapis

BMP 8bit odczyt/zapis
LW
  • Rejestracja:około 6 lat
  • Ostatnio:około 4 lata
  • Postów:19
0

Cześć, od paru dni męczę się z pewnym problemem. Mianowicie próbuje rozgryźć to jak odczytać-zmodyfikować-zapisać obraz w formacie bitmapy... Szukam sporo w internecie i nie mogę znaleźć a sam aż tak tego nie rozumiem :(. Mam taki program napisany. Odczyt działa tylko nie wiem jak to zapisać do pliku. Wiem, że każdy element powstałej tablicy to kolejny bit obrazu więc wystarczy je zmodyfikować.

Kopiuj
#include <iostream>
#include <Windows.h>
#include <fstream>
 
using namespace std;
 
 
uint8_t* datBuff[2] = { nullptr, nullptr }; 
uint8_t* pixels = nullptr; 
BITMAPFILEHEADER* bmpHeader = nullptr; 
BITMAPINFOHEADER* bmpInfo = nullptr; 
int LoadBMP(const char* location);
int SaveNewBmp(const char* location);
 
int main()
{
   
 
 
     
 
}
 
 
 
int LoadBMP(const char* location) {
 
 
 
    std::ifstream file(location, std::ios::binary);
    if (!file)
    {
        std::cout << "Failure to open bitmap file.\n";
 
        return 1;
    }
 
 
 
    datBuff[0] = new uint8_t[sizeof(BITMAPFILEHEADER)];
    datBuff[1] = new uint8_t[sizeof(BITMAPINFOHEADER)];
 
    file.read((char*)datBuff[0], sizeof(BITMAPFILEHEADER));
    file.read((char*)datBuff[1], sizeof(BITMAPINFOHEADER));
 
 
    bmpHeader = (BITMAPFILEHEADER*)datBuff[0];
    bmpInfo = (BITMAPINFOHEADER*)datBuff[1];
 
 
 
    if (bmpHeader->bfType != 0x4D42)
    {
        std::cout << "File \"" << location << "\" isn't a bitmap file\n";
        return 2;
    }
 
    pixels = new uint8_t[bmpInfo->biSizeImage];
 
    file.seekg(bmpHeader->bfOffBits);
    file.read((char*)pixels, bmpInfo->biSizeImage);
     
 
 
    return 0;
}
 
int SaveNewBmp(const char* location) {
    std::ofstream file(location, std::ios::binary);
    if (!file)
    {
        std::cout << "Failure to open bitmap file.\n";
 
        return 1;
    }
 
    file.write((char*)datBuff[0], sizeof(BITMAPFILEHEADER));
    file.write((char*)datBuff[1], sizeof(BITMAPINFOHEADER));
 
    file.write((char*)pixels, bmpInfo->biSizeImage);
    return 0;
}

Jakieś pomysły? Z góry dziękuję :D

edytowany 1x, ostatnio: Ludwiniak Wiktor
MarekR22
Moderator C/C++
  • Rejestracja:ponad 17 lat
  • Ostatnio:2 minuty
2

Nigdy nie używałem ale powinno odpowiadać twoim potrzebom: http://easybmp.sourceforge.net/


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
LW
Niestety ale zależny mi na tym aby nie korzystać z zewnętrznych gotowych rozwiązań. Jedynie biblioteki standardowe
TomaszLiMoon
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 14 godzin
  • Postów:530
0
LW
Niestety ale nie działa dla plików z pixelami 8bit
DA
  • Rejestracja:ponad 6 lat
  • Ostatnio:3 dni
  • Postów:142
0

Pytanie co chcesz konkretnie zrobić. Chcesz używać z WinApi, czy tylko mieć dostęp do poszczególnych pikseli. W tym drugim przypadku ważne jest wyrównanie wierszy pikseli do DWORD.

Kopiuj
#define	ROUND_DWORD( w )	( ( w + 3 ) & ( ~3 ) )
switch ( bmpInfo->biBitCount )
{
case 24:	bytes_per_line = ROUND_DWORD( bmpInfo->biWidth* 3 );		break;
case 8:	bytes_per_line = ROUND_DWORD( bmpInfo->biWidth);			break;
case 4:	bytes_per_line = ROUND_DWORD( ( bmpInfo->biWidth+ 1 ) / 2 );	break;
case 1:	bytes_per_line = ROUND_DWORD( ( bmpInfo->biWidth+ 7 ) / 8 );	break;
default:
	NOT SUPORTED
	break;
}

Dla ośmiu bitów, pobranie piksela

Kopiuj
int GetPixel8( int x, int y )
{
	// Bitmapa w BMP jest do góry nogami.
	int	y = bmpInfo->biHeight - y - 1;
	return pixels[ x + bytes_per_line * y ];
}

Gdybyś wczytał paletę kolorów mógłbyś użyć

Kopiuj
void GetPixel8RGB( int& r, int& g, int& b, int x, int y )
{
	RGBQUAD	rgb = bmpInfo->bmiColors[ GetPixel8( x, y ) ];
	r = rgb.rgbRed;
	g = rgb.rgbGreen;
	b = rgb.rgbBlue;
}

Żeby wczytać paletę musisz zaalokować więcej pamięci
Wiersz 30

Kopiuj
datBuff[1] = new uint8_t[sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD) ];

Policzyć ile kolorów

Kopiuj
	int	num_colors = 0;
	if ( bmpInfo->biBitCount <= 8 )
	{
		num_colors = bmpInfo->biClrUsed;
		if ( num_colors == 0 )
			num_colors = ( 1 << bmpInfo->biBitCount );
	}

Zmienić wczytywanie headera
Wiersz 33

Kopiuj
file.read((char*)datBuff[1], sizeof(BITMAPINFOHEADER) + num_colors*sizeof(RGBQUAD));

Mogą być błędy, bo wyciągnąłem to istniejącego programu, ale musiałem pozmieniać dla Twoich nazw i architektury.

edytowany 3x, ostatnio: -daniel-
LW
Dokładniej to chce wczytać obrazek nałożyć kontrast i zapisac
DA
W ośmiobitowym kontrast możesz zmieniać manipulując paletą. Chyba, że masz na myśli wyostrzanie.
LW
Tylko jak wczytać aby mieć dostęp do palety i co gorsza zapisać?
DA
Zarys wczytywania palety jest powyżej. Tak samo zapisujesz. Zadbaj o pola bmpHeader->bfSize bmpHeader->bfOffBits i bmpInfo->biClrUsed. Z zapisem 8 bitów to muszę poszukać, bo mój program z którego wyciągnąłem kod zapisuje tylko 24 bity.
Stefan_3N
  • Rejestracja:około 6 lat
  • Ostatnio:około 2 miesiące
  • Postów:145
0

Windowsowy plik w formacie bmp składa się z czterech części.

  1. Na początku pliku jest nagłówek BITMAPFILEHEADR
    https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapfileheader
    Przy zapisie tego nagłówka interesują Ciebie trzy pola:

bfType - to jest WORD (czyli dwa bajty). Pierwszy z nich musi mieć wartość 0x42, a drugi 0x4D (to odpowiada znakom BM)
bfSize - to jest DWORD i tu musisz wpisać całkowity rozmiar pliku. Musisz albo wyliczyć rozmiar pliku, albo plik zapisać, sprawdzić jego rozmiar, cofnąć się do tego miejsca w pliku i wpisać odpowiednia wartość.
bfOffBits to jest DWORD i tu musisz wpisać przesunięcie od początku pliku do miejsca, gdzie w pliku zaczynają się dane dotyczące pikseli. I tu jak wyżej, albo trzeba to policzyć, albo w czasie zapisywania na bieżąco to wpisywać.

  1. Bezpośrednio za BITMAPFILEHEADER w pliku jest zapisana struktura BITMAPINFOHEADER.
    https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader

  2. Ten element jest opcjonalny i jest to tablica kolorów (o zmiennej długości) wartości typu RGBQUAD
    https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-rgbquad
    RGBQUAD - to cztery bajty (jeden DWORD).

  3. Właściwe dane (treść obrazu) - jest to tablica bajtów o zmiennej długości.

Konkretna zawartość pliku (to czy element 3 występuje) zależy od organizacji bitmapy.
W nagłówku BITMAPINFOHEADER jest pole biBitCount. Jeśli biBitCount jest mniejsze od 16, tzn. że bitmapa zawiera tablicę kolorów (czyli punkt 3 wymieniony wyżej występuje). W takim wypadku liczba elementów w tablicy kolorów jest 2 do potęgi biBitCount.
Jeśli biBitCount jest większe lub równe 16, to element 3 (tablica kolorów nie występuje).

Jak obliczyć całkowitą objętość danych oraz przesunięcie do danych z pikselami (dane potrzebne do wstawienia do BITMAFILEHEADER)?

Trzeba zacząć od policzenia objętości jednej linii obrazu.
W formacie bmp długość linii obrazu w bajtach jest zaokrąglana do czterech bajtów w górę. Do wyliczenia używamy funkcji

Kopiuj
long ScanBytes(int pixWidth, int bitsPixel) {
	return (((long)pixWidth*bitsPixel+31) / 32) * 4;
}

gdzie:
pixWidth - szerokość obrazu w pikselach
bitsPixel - wspomniane wyżej biBitCount

Więc objętość jednej linii to:

Kopiuj
long w=ScanBytes(.., ..)

Objętość całego bloku z danymi - to w*h (gdzie h - to wysokość obrazu).
Do tego trzeba dodać sizeof(BITMAPFILEHEADER) i sizeof(BITMAPINFOHEADER).
I jeśli biBitCount jest mniejsze od 16, to do tego dodajemy (2 do potęgi biBitCount) razy sizeof (RGBQUAD).
Tak wyliczoną wartość wstawiasz do bfSize.
Natomiast do bfOffBits wstawiasz to co wyliczyliśmy wyżej, ale minus objętość bloku z danymi (czyli tylko rozmiary nagłówków i ewentualnie rozmiar tablicy kolorów - jeśli występuje)

BITMAPFILEHEADR plus następująca po nim tablica kolorów RGBQUAD (jeśli występuje) traktowana jest jak jedna struktura typu BITMAPINFO.
Stąd nie przewiduje się, żeby po BITMAPFILEHEADER, a przed tablicą kolorów był jakiś odstęp. Czyli jeśli tablica kolorów występuje, to zapisujesz w pliku po kolei BITMAPFILEHEADER, BITMAPINFOHEADER i bezpośrednio za tym - po kolei po cztery bajty na kolor.

Po zapisaniu całej tablicy kolorów od razu od kolejnego bajtu możesz zacząć zapisywać dane dotyczące pikseli. To miejsce (jego położenie w pliku) jest właśnie wskazane w nagłówku (pole bfOffBits).

Spróbuj napisać to sam, jeśli nie ogarniesz - daj znać.


edytowany 2x, ostatnio: Stefan_3N
DA
  • Rejestracja:ponad 6 lat
  • Ostatnio:3 dni
  • Postów:142
0

Zapis do BMP w moim bardzo starym programie wyglądał tak:

Kopiuj
	int	file_size = 14 + 40;	// Don't use sizeof(), aligment.
	int	bitmap_size = 0;
	int	bytes_per_line = ROUND_DWORD( width * 3 );
	int	bit_count = 24;

	switch ( num_colors )
	{
	case 2:
		file_size += 2 * 4;
		bytes_per_line = ROUND_DWORD( ( width + 7 ) / 8 );
		bit_count = 1;
		break;
	case 16:
		file_size += 16 * 4;
		bytes_per_line = ROUND_DWORD( ( width + 1 ) / 2 );
		bit_count = 4;
		break;
	case 256:
		file_size += 256 * 4;
		bytes_per_line = ROUND_DWORD( width );
		bit_count = 8;
		break;
	}

	bitmap_size = bytes_per_line * height;
	file_size += bitmap_size;

	BITMAPFILEHEADER	bmp_file_header = { 0 };
	BITMAPINFOHEADER	bmp_info_header = { 0 };

	bmp_file_header.bfType = 0x4d42;
	bmp_file_header.bfSize = file_size;
	bmp_file_header.bfReserved1 = 0;
	bmp_file_header.bfReserved2 = 0;
	bmp_file_header.bfOffBits = file_size - bitmap_size;

	file.WriteBytes( &bmp_file_header, 14 );

	bmp_info_header.biSize = 40;
	bmp_info_header.biWidth = width;
	bmp_info_header.biHeight = height;
	bmp_info_header.biPlanes = 1;
	bmp_info_header.biBitCount = bit_count;
	bmp_info_header.biCompression = BI_RGB;
	bmp_info_header.biSizeImage = bitmap_size;
	bmp_info_header.biXPelsPerMeter = 0;
	bmp_info_header.biYPelsPerMeter = 0;
	bmp_info_header.biClrUsed = num_colors;
	bmp_info_header.biClrImportant = important_colors; // Maybe 0, maybe num_colors

	file.WriteBytes( &bmp_info_header, 40 );

	// Color map.
	if ( num_colors != 0 )
	{
		file.WriteBytes( palette, num_colors * sizeof(RGBQUAD) );
	}

	// Bitmap. Assumes that lines are properly aligned.
	file.WriteBytes( pixels, height * bytes_per_line );
edytowany 4x, ostatnio: -daniel-

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.