problem z przetworzeniem bitmapy

0

Witam!
Jak w temacie problem dotyczy przetworzenia kolorowej bitmapy na bitmapę w skali szarości. W efekcie końcowym w folderze mają się znajdować dwie bitmapy.
Wydaje mi się, że kod jest skończony ale jak chce wczytać przykładowo 256 kolorową bitmapę to program przestaje działać.
Nie mam pojęcia gdzie wkradł mi się błąd. Środowisko w jakim pisze - Dev C++.
Oto kod:

#include <iostream> 
#include <cstdlib>
#include <fstream> 
#include <windows.h>


using namespace std;



struct Pixel
{   //odwrotna kolejnosc
    unsigned char R; 
    unsigned char G; 
    unsigned char B; 
}; 


int main()
{  
    
   COLORREF kolor;
   HDC hdc;
   int a, b, c;
   char szary;  
   
   
   ifstream ifs("nazwa_pliku.bmp", ios::binary); 

          if(ifs.good() == true )
          {
          std::cout << "Uzyskano dostep do pliku..." << std::endl;
          } else std::cout << "Brak dostepu do pliku..." << std::endl;
   
                                                    
   char* temp = new char[sizeof(BITMAPFILEHEADER)];
   ifs.read(temp, sizeof(BITMAPFILEHEADER));
   BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)(temp);
 
   delete[] temp;

   temp = new char[sizeof(BITMAPINFOHEADER)];
   ifs.read(temp, sizeof(BITMAPINFOHEADER));
   BITMAPINFOHEADER* bih = (BITMAPINFOHEADER*)(temp);

   delete[] temp;

   ifs.seekg(bfh->bfOffBits, ios::beg); // wskazanie na początek danych obrazka
   
   int width = bih->biWidth;
   if(width % 4) width += 4 - (width % 4); // piksele są wyrównywane do 4 bajtów
   
   Pixel** pixs = new Pixel*[bih->biHeight];
   for(int i=0; i<bih->biHeight; i++)
   {
   temp = new char[3*width];
   ifs.read(temp, 3*width);
   pixs[i] = (Pixel*)(temp);
   
   delete[] temp;
   }   
   
   
    for (int i=0;i<bih->biHeight;i++)
    {
        for (int j=0;j<bih->biWidth;j++)
        {
            kolor=GetPixel(hdc, i,j);
            pixs[i][j].R=GetRValue(kolor);
            pixs[i][j].G=GetGValue(kolor);
            pixs[i][j].B=GetBValue(kolor);
            
            szary= (pixs[i][j].R+pixs[i][j].G+pixs[i][j].B)/3;
            kolor=RGB(szary,szary,szary);
        }    
    };
    
   ofstream ofs("nazwa_pliku2.bmp", ios::binary);
    
   ofs.write((char*)(bfh), sizeof(BITMAPFILEHEADER));//char* file , sizeof File 
   ofs.write((char*)(bih), sizeof(BITMAPINFOHEADER));
   
        
        
   delete bfh;
   delete bih;
   for(int i=0; i<bih->biHeight; i++) delete[] pixs[i];
   delete[] pixs;
 
 
    system ("pause");
    return 0;
}

0

Może nie masz prawa odczytu/zapisu, a nie masz żadnej obsługi błędów - jak nie masz dostępu to tylko komunikat leci, a dalej kod się wykonuje. Sprawdź debugger'em dokładnie gdzie się program wysypuje.

0

Nie jestem obeznany z CPP ale rzuca mi się w oczy coś takiego:

   // alokujesz sobie określoną ilość pamięci, wskazuję na nią TEMP
   char* temp = new char[sizeof(BITMAPFILEHEADER)];
   // wczytujesz do zaalokowanej pamięci konkretną liczbę bajtów danych
   ifs.read(temp, sizeof(BITMAPFILEHEADER));
   // BFH ma wskazywać na to samo co TEMP
   BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)(temp);
   // kasujesz to na co wskazywał TEMP (oraz tymsamym BFH)
   delete[] temp;

Potem używasz danych spod bfh.

W ogóle może powiesz czym objawia się to "niedziałanie programu"?

0

Polecałbym nie tworzyć koła od nowa.

Bitmapy (a ściśle rodzina DIB - http://en.wikipedia.org/wiki/Device-independent_bitmap) występują w kilkunastu odmianach, w życiu tego sam wszystkiego nie obsłużysz.

Zastanów się nad skorzystaniem z gotowego kodu: http://www.kalytta.com/bitmap.h

0

Może nie masz prawa odczytu/zapisu, a nie masz żadnej obsługi błędów - jak nie masz dostępu to tylko komunikat leci, a dalej kod się wykonuje. Sprawdź debugger'em dokładnie gdzie się program wysypuje.

Ok, powrzucałem kod w ify:

 
     ofstream ofs("plik.bmp", ios::binary); // otwieramy plik do zapisu binarnego
    
     if(ifs.good() == true )
     {
     std::cout << "Uzyskano dostep do pliku..." << std::endl;
          
          ofs.write((char*)(bfh), sizeof(BITMAPFILEHEADER));//char* file , sizeof File 
          ofs.write((char*)(bih), sizeof(BITMAPINFOHEADER));
   
     } else std::cout << "Brak dostepu do pliku..." << std::endl;

Potem używasz danych spod bfh.

Faktycznie, jednakże nadal program przestaje działać.

  
   char* temp = new char[sizeof(BITMAPFILEHEADER)];
   ifs.read(temp, sizeof(BITMAPFILEHEADER));
   BITMAPFILEHEADER* bfh = (BITMAPFILEHEADER*)(temp);
   
   delete bfh;
   delete[] temp;

   temp = new char[sizeof(BITMAPINFOHEADER)];
   ifs.read(temp, sizeof(BITMAPINFOHEADER));
   BITMAPINFOHEADER* bih = (BITMAPINFOHEADER*)(temp);
   
   delete bih;
   delete[] temp;

   ifs.seekg(bfh->bfOffBits, ios::beg); 
   
   int width = bih->biWidth;
   if(width % 4) width += 4 - (width % 4); 
   
   Pixel** pixs = new Pixel*[bih->biHeight];
   for(int i=0; i<bih->biHeight; i++)
   {
   temp = new char[3*width];
   ifs.read(temp, 3*width);
   pixs[i] = (Pixel*)(temp); 
   
   delete[] pixs[i];
   delete[] temp;
   }   
   delete[] pixs;
  

W ogóle może powiesz czym objawia się to "niedziałanie programu"?

Włącza mi się program z komunikatem "Uzyskano dostęp do pliku" po czym wyskakuje okienko systemowe Program .exe przestał działać...

0

Odświeżam trochę temat bo jeszcze nie pozbyłem się problemu. Mianowicie program wczytuje obydwa pliki, tyle że problem pojawia się przy zapisie. Tak jakby kolory się nie przepisywaly. Gdy stosuję funkcję SetPixel(kod w komentarzach), końcowy obrazek zajmuje tylko 4kb, podczas gdy początkowy ma 52kb. Natomiast gdy stosuję funkcję bitmapa.write(przypisanie tablicy ze zmienionym kolorem pixela) pamięc wzrasta trzykrotnie... Obydwa pliki po zapisie są oczywiście "uszkodzone" tzn. brak obrazka.
Moje pytanie co do programu.

  1. Czy w kodzie powinienem korzystać z wczytywania dwóch plikow fstreamem
    (ifstream/ofstream bitmapa("nazwa_pliku.bmp", ios::in/ios::out | ios:: binary))
    czy też może powinienem je wczytać funkcją :
    HBITMAP bitmapa=( HBITMAP )LoadImage(NULL, "nazwa_pliku.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE)?
    Pytam o to ponieważ korzystając z GetPixel i SetPixel muszę użyć uchwytów HDC i nie wiem do końca czy zapis jest w pełni prawidłowy (być może coś robię źle)
    Najgorsze jest to, że program, kompiluje się całkowicie a powstały obrazek jest uszkodzony.

Jest to mój pierwszy program tego rodzaju i męczę się z nim od kilku ładnych dni, dlatego będę bardzo wdzięczny za każdy przydatny komentarz.

Dołączam kod:

 

struct Pixel
{  
    unsigned char R; 
    unsigned char G; 
    unsigned char B; 
};

struct wym
{
       int szerokosc;
       int wysokosc;
}wym;


int main()
{

COLORREF kolor;
   char szary;
   
   BITMAPFILEHEADER* bfh;
   BITMAPINFOHEADER* bih;
   
   Pixel** pixs;
   Pixel** pixs2;
   
   
   ifstream ifs("mario2.bmp", ios::in | ios::binary); // otwieramy plik do odczytu binarnego
   ofstream ofs("mario3.bmp", ios::out | ios::binary | ios::trunc); // otwieramy plik do zapisu binarnego

   if(ofs.good() == false) cout << "plik do zapisu nie otwarty...\n";
   else if(ifs.good() == true )
   {
   cout << "Uzyskano dostep do obu plikow..." <<endl;             
   
   
   ifs.seekg(18,ios::beg); 
   ifs.read((char*)&wym.szerokosc, sizeof(int)); //wczytanie wartości szerokości
   ifs.seekg(22,ios::beg);
   ifs.read((char*)&wym.wysokosc, sizeof(int)); //wczytanie wartości wysokości

   
   //tymczasowy bufor na dane
   char* temp = new char[sizeof(BITMAPFILEHEADER)];
   ifs.read(temp, sizeof(BITMAPFILEHEADER));//
   bfh = (BITMAPFILEHEADER*)(temp);// rzutujemy zawartość bufora na typ BITMAPFILEHEADER
   ofs.write((char*)(bfh), sizeof(BITMAPFILEHEADER));
   
   delete[] temp;
   

   temp = new char[sizeof(BITMAPINFOHEADER)];
   ifs.read(temp, sizeof(BITMAPINFOHEADER));
   bih = (BITMAPINFOHEADER*)(temp);
   ofs.write((char*)(bih), sizeof(BITMAPINFOHEADER));
   
   delete[] temp;
   

   int width = wym.szerokosc;
   if(width % 4) width += 4 - (width % 4); // piksele w bitmapie są wyrównywane do 4 bajtów
   
   
     //Tworzymy tablice dwuwymiarowa struktur

		pixs = new Pixel*[wym.wysokosc];
   
                    for(int i=0; i<wym.wysokosc; i++)
                    {
                             temp = new char[3*width]; //3 skladowe koloru wczytujemy do tablicy
                             ifs.read(temp, 3*width);
                             pixs[i] = (Pixel*)(temp); 
                    }
                    
           	pixs2 = new Pixel*[wym.wysokosc];
		
                    for(int i=0; i<wym.wysokosc; i++)
                    {
                             temp = new char[3*width]; //3 skladowe koloru wczytujemy do tablicy
                             ifs.read(temp, 3*width);
                             pixs2[i] = (Pixel*)(temp);
                    }          

   ifs.seekg(bfh->bfOffBits, ios::beg); // wskazuje początek danych obrazka
   ofs.seekp(bfh->bfOffBits, ios::beg);//
   
  /* HDC hdc = CreateCompatibleDC( NULL );
   SelectObject(hdc,ifs);
   HDC hdc2 = CreateCompatibleDC( NULL );
   SelectObject(hdc2,ofs);*/
       for (int i=0;i<wym.wysokosc;i++)
       {
           for (int j=0;j<wym.szerokosc;j++)
           {
               kolor=GetPixel(hdc, i,j);
               pixs[i][j].R=GetRValue(kolor);
               pixs[i][j].G=GetGValue(kolor);
               pixs[i][j].B=GetBValue(kolor);
            
               szary= (pixs[i][j].R+pixs[i][j].G+pixs[i][j].B)/3;
              
               kolor=RGB(szary,szary,szary);
               
            
               /*SetPixel(hdc2, i, j, RGB(szary,szary,szary));*/
               
                Pixel *chwilowy;
		chwilowy = &pixs[i][j];
		char *znaki;
		znaki = (char *)(chwilowy);
		ofs.write(znaki, sizeof(Pixel));//jak podziele sizeof(Pixel) przez 3 to 
           }                                           //zapisana bitmapa bedzie zajmowala tyle samo miejsca
       }; 

	// czyszczenie  
	for(int i=0; i<bih->biHeight; i++) delete[] pixs[i];
        delete[] pixs;
    
   } else cout << "Brak dostepu do pliku..." <<endl;   
    
    ofs.close(); 	
    ifs.close();
 
   system ("PAUSE");
   return 0;
}

0

Ja jak miałem problem z bitmapami (co prawda w asmie), to zastosowałem taką metodę - tworzyłem obrazek 4x4 piksele potem:

  • za pierwszym razem napisałem tylko kopiowanie
  • w kolejnym podejściu jakieś tam przetworzenia

Potem tylko HexEdytor i dość szybko można było zobaczyć o co chodzi i co jest nie tak.

0

Ten Twój kod to straszna kupa.

ifs.seekg(18,ios::beg); 
ifs.read((char*)&wym.szerokosc, sizeof(int)); //wczytanie wartości szerokości
ifs.seekg(22,ios::beg);
ifs.read((char*)&wym.wysokosc, sizeof(int)); //wczytanie wartości wysokości
 
...
 
ifs.read(temp, sizeof(BITMAPFILEHEADER));//
 
...

Najpierw czytasz szerokosc i wysokosc, później czytasz strukturę BITMAPFILEHEADER, która leży na początku pliku. Problem w tym że zapomniałeś przesunąć wskaźnik odczytu na początek, bo teraz temp na pewno nie zawiera poprawnym danych. Jak się zabierasz za czytanie plików BMP, wpierw wypadałoby zapoznać się z ich formatem.

Czytanie bitmapy może wyglądać tak:

BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD* bmiColors; 

...

ifs.read(&bmfh, sizeof(BITMAPFILEHEADER));
ifs.read(&bmih, sizeof(BITMAPINFOHEADER));

/* czyta paletę, jeśli takowa występuje */
if(bmih.biClrUsed > 0)
{
	bmiColors = new RGBQUAD[bmih.biClrUsed];
	ifs.read(bmiColors, bmih.biClrUsed * sizeof(RGBQUAD));
}


ifs.seekg(bmfh.bfOffBits);

/* tu czytasz kolejne piksele */
int width = wym.szerokosc;
if(width % 4) width += 4 - (width % 4); // piksele w bitmapie są wyrównywane do 4 bajtów

...

temp = new char[3 * width]; //3 skladowe koloru wczytujemy do tablicy

Kolega chyba nie zrozumiał, po co te magiczne obliczenia przy width ;) Zmienna ta powinna zawierać szerokość bitmapy w bajtach, ponieważ wiersz bitmapy może mieć wielkość bajtową większą niż wskazywałaby szerokość wyrażona w pikselach. Czyli kod powinien wyglądać tak:

int width = wym.szerokosc * 3; //<--- to jest poprawne TYLKO dla bitmap 24 bitowych
if(width % 4) width += 4 - (width % 4);

...

temp = new char[width]; 
ifstream ifs(...);
ofstream ofs(...);

...

SelectObject(hdc, ifs);
...
SelectObject(hdc2, ofs);

WTF?!

1 użytkowników online, w tym zalogowanych: 0, gości: 1