logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Jak odjąć jeden obraz .bmp od drugiego w C/C++ na Windows?

simba85 21 Sie 2007 16:54 8704 21
  • #1 4200409
    simba85
    Poziom 11  
    Posty: 12
    Ocena: 1
    W skrócie sprawa wygląda tak - będę miał obraz .bmp z kamerki 640x480 pikseli, w 8-bitowych odcieniach szarości. Chcę mieć możliwość odejmowania jednego zdjęcia od drugiego.

    Jestem początkujący, więc sprawę wyobrażałem sobie tak:
    Na pewno istnieje jakaś fajna biblioteka, załączę kilka plików .h, a pliki .c z nimi powiązane będą miały jakieś funkcje typu "odczyt" i "zmiana" wartości piksela. Więc chciałem otworzyć obrazek, przebiec w dwóch pętlach wszystkie jego piksele i przy okazji je modyfikować, odejmując zapamiętane wcześniej wartości pikseli z obrazka, który będę chciał odejmować od reszty. Potem zapisać nowy obrazek i tyle.

    W teorii wszystko ładnie i prosto. Ale: po pierwsze nie znalazłem biblioteki z takimi funkcjami, a biblioteki do odczytu .bmp jakie znalazłem (devil) raczej średnio łapię. Na, kiedy próbuję załączyć pliki .h z tej biblioteki, Dev-C++ mówi, że wg. ISO C++ to on nie może czegośtam zrobić. Acha, program ma działać w Windowsami (także 98 ).

    Czy macie jakieś pomysły? Próbowałem skorzystać z wyszukiwarki, ale połaczenie jest "reset" i nic się nie wyświetla.
  • #2 4200615
    Arrow
    Poziom 12  
    Posty: 102
    Pomógł: 1
    Ocena: 1
    Przyłączam się do pytania kolegi simba85
    Chodzi mi o obsługe kamery internetowej i jednoczesnej analizy obrazu, może to być przykład w C++ builder 6, w visual c++ i c#

    Pozdrawiam.
  • Pomocny post
    #3 4202092
    maciek_slon
    Poziom 29  
    Posty: 942
    Pomógł: 143
    Ocena: 27
    wg mnie całkiem szybkie byłoby skorzystanie ze standardowych funkcji fread a nawet z fgetc. Otwieramy oba pliki do odczytu jako tekstowe, wczytujemy z nich wielkość nagłówka, wielkość obrazu jest znana więc nie trzeba tego wczytywać, przeskakujemy do pierwszego bajtu danych obrazu i w obu plikach jedziemy po pikselach po kolei.

    Mało jasno chyba napisałem ale już późno, może ktoś będzie w stanie rozwinać moją myśl :P

    Na pocieszenie:
    http://www.fastgraph.com/help/bmp_header_format.html

    Dodano po 1 [minuty]:

    W zasadzie począątek obrazka możńa wczytać tylko raz a potem zapisać go jako stałą w programie, bo skoro wszystkie obrazy będą pochodziły z tego samego źródła to ich nagłówki będą tej samej wielkości - a co za tym idzie dane obrazka będą się zaczynały w tym samym miejscu.

    Dodano po 1 [minuty]:

    A, i jeszcze jedno - będzie to raczej metoda szybsza w działaniu niż korzystanie z dodatkowych bibliotek w których funkcja zwracająca kolor danego piksela będzie działała dużo wolniej niż przeskakiwanie o zawsze o jeden bajt do przodu w mojej metodzie. :-)

    Dodano po 24 [minuty]:

    
    #include <stdio.h>
    #include <stdlib.h>
    
    FILE * first, *last;
    int data_offset;
    
    int main()
    {
    	int data_start;
    	unsigned char d1, d2;
    	
    	// otwieramy pierwszy plik do odczytu binarnego
    	first = fopen("plik1.bmp", "rb");
    	// otwieramy drugi plik do odczytu i modyfikacji (tez binarnie)
    	last = fopen("plik2.bmp", "rb+");
    	
    	// przechodzimy do offsetu 10 - tam jest zapisany adres początku danych obrazka
    	fseek(first, 10, SEEK_SET);
    	// wczytujemy do inta poczatek sekcji danych obrazka
    	fread(&data_start, sizeof(int), 1, first);
    	
    	// w obu plikach przechodzimy do wczytanego wczesniej adresu
    	fseek(first, data_start, SEEK_SET);
    	fseek(last, data_start, SEEK_SET);
    	
    	for (int x = 0; x<640; x++)
    		for (int y = 0; y<480; y++)
    		{
    			// z obu plikow wczytujemy kolor piksela
    			fread(&d1, 1, 1, first);
                fread(&d2, 1, 1, last);
                
                // odejmujemy drugi od pierwszego
                d2 = d1 - d2;
                
                // w pliku do ktorego zapisujemy cofamy sie o jedna pozycje
                // poniewaz chcemy zapisac w miejscu ktore przed chwila odczytalismy
                fseek(last, -1, SEEK_CUR);
                
                // i zapisujemy zmodyfikowany kolor
                fwrite(&d2, 1, 1, last);
    		}
    	
    	// zamykamy oba pliki
    	fclose(first);
    	fclose(last);
    	
    	return 0;
    }
    


    Dodano po 2 [minuty]:

    takie coś mi się udało na szybko stworzyć, z mała modyfikacją - pliki otwieram nie w trybie tekstowym tylko w trybie binarnym.

    teraz należy tylko to rozbudować - dopisać obsługę sytuacji w której wynik odejmowania nie mieści się w zakresie - czyli np. 18 - 125.

    dla sprawdzenia użyłem dwóch białych obrazków, w wyniku otrzymałem obrazek czarny - czyli to czego się spodziewaliśmy :-)

    Dodano po 2 [minuty]:

    a, i jeszcze jedna uwaga :P ta metoda działa tylko dla plików w których paleta barw jest uporządkowana - tzn. kolor nr 0 oznacza całkowitą czerń, a kolor nr 255 oznacza całkowitą biel, a pośrednie po kolei kolejne odcienie szarości. JEśli w palecie coś jest namieszane to trzeba trochę rozwinać tą metodę - wczytać paletę z pliku do tablicy, wczytywać z tej tablicy dane koloru piksela, wykonać odejmowanie, znaleźć w palecie kolor który nam wyszedł w wyniku a następnie dopiero zapisać go do pliku - w tej sytuacji z punktu widzenia łatwości zaprogramowania tego lepiej już wykorzystać jakąś gotową bibliotekę która wszystkie te konwersje wykona za nas :P
  • #4 4202420
    simba85
    Poziom 11  
    Posty: 12
    Ocena: 1
    Dzięki, przetestuję dzisiaj Twoje rozwiązanie ;-)

    A co znaczą parametry rb i rb+?
  • #6 4202870
    Fyszo
    Poziom 37  
    Posty: 3987
    Pomógł: 223
    Ocena: 115
    8-bitowe obrazki posiadają coś takiego jak paleta kolorów. 2 oddzielne obrazki beda miały przy tej samej wartości piksela (np. 4Dh) całkowicie różne kolory, bo pierwszy w palecie bedzie posiadał dla wartości 4Dh - kolory FFxFFxFF (RGB) a drugi może mieć dla 4Dh - kolory 00x00x00 (RGB). Musisz zestandaryzowac palete i przerobić według niej wszystkie obrazki aby miały jednakową tablicę kolorów.
  • #7 4203060
    maciek_slon
    Poziom 29  
    Posty: 942
    Pomógł: 143
    Ocena: 27
    Pisałem o tym - ale przyjąłem założenie że skoro wszystkie obrazki będą pochodziły z jednego źródła to będą miały taką samą paletę :P Gdyby pytający zamieścił dwa przykłądowe obrazki można by było wszystko szybko sprawdzić :-)
  • #8 4204391
    simba85
    Poziom 11  
    Posty: 12
    Ocena: 1
    Obrazków jeszcze nie mam, bo układ pomiarowy się buduje ;-). Ale wszystkie obrazki będą z jednego źródła, więc z tego co mówicie, to powinno działać ;-).
  • #9 4204880
    maciek_slon
    Poziom 29  
    Posty: 942
    Pomógł: 143
    Ocena: 27
    Zależy właśnie czy urządzenie "robiące" obrazki zapisuje paletę uporządkowaną (od czerni do bieli) czy mieszaną (wtedy trzeba dopisać funkcję normalizującą paletę - ale po przemyśleniu nie jest to dużo roboty :-) )
  • #10 4205881
    simba85
    Poziom 11  
    Posty: 12
    Ocena: 1
    Jutro się popytam, może mają jakieś stare obrazki z tej kamerki. Nie mam szczerze mówiąc pojęcia o rodzajach palet, ale jakby coś to się zgłoszę ;-). Bo rozumiem, że uporządkowana to będzie (8 bitów) dla czarnego 00000000, dla najbardziej szarego 10000000, ..., dla najmniej szarego 11111110, a dla białego 11111111? Pomijam fakt, z której strony jest najbardziej znaczący bit, bo chyba nie ma to znaczenia :D.
  • #11 4206253
    maciek_slon
    Poziom 29  
    Posty: 942
    Pomógł: 143
    Ocena: 27
    No mniej więcej o to chodzi, z pominięciem tego "najbardziej szarego" :P
  • #12 4208042
    simba85
    Poziom 11  
    Posty: 12
    Ocena: 1
    No to programik jednak nie działa :-(. Mam obrazek z kamerki i próbowałem go skopiować i odjąć od samego siebie - czyli powinienem dostać czarność, a dostaję coś takiego:

    Dodano po 5 [minuty]:

    Coś mam chyba brzydko utawione, bo zdjęcia mi się nie ładują :-(.

    Dodano po 8 [minuty]:

    Jeszcze jedna próba, tym razem jako jpeg.
    Załączniki:
    • Jak odjąć jeden obraz .bmp od drugiego w C/C++ na Windows? Kopia 515.1.jpg (147.06 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • #13 4208085
    Fyszo
    Poziom 37  
    Posty: 3987
    Pomógł: 223
    Ocena: 115
    Jak nie znasz sie za bardzo na budowie plików BMP to lepiej wykorzystac gotowe funkcje pobierające piksel. Albo załaduj segmenty 'data' do tablic 640x480 a potem odejmowanie bedzie zrobione szybko.

    Czy tlo obrazka różnicowego przypomina obrazek wzorcowy? Jeśli tak to albo masz zły algorytm, albo juz podczas wczytywania danych jest problem z paletą/kolorami.
  • #14 4208093
    simba85
    Poziom 11  
    Posty: 12
    Ocena: 1
    Po dodaniu w pętli warunku:

    if((syg-tlo)>0) syg=syg-tlo;
    else continue;

    nie dzieje się nic, tzn. obrazek zostaje jaki był (czyli jak poniżej). To znaczy, że za każdym razem przekraczamy zakres, tylko jak to możliwe? Acha, po przejrzeniu zapodanej stronki widzę, że funkcja fread zwraca rozmiar pliku, więc jak to właściwie działa ;-) ?
    Załączniki:
    • Jak odjąć jeden obraz .bmp od drugiego w C/C++ na Windows? Kopia (2) 515.1.jpg (44.69 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • #15 4208098
    simba85
    Poziom 11  
    Posty: 12
    Ocena: 1
    Fyszo, czy znasz jakąś bibliotekę z gotowymi takimi funkcjami? Byłbym wdzięczny ;-).
  • #16 4208132
    Fyszo
    Poziom 37  
    Posty: 3987
    Pomógł: 223
    Ocena: 115
    Windows API. Najlepiej udokumentowane. Użyj jakiegoś pictureboxa itp. Jest sporo metod dla takich kontrolek.

    Kiedys sporo robilem rzeczy z plikami BMP ale to było w czasach DOSa i juz nieaktualne. Takze mam rożne moduły ale pod VESĘ.
  • #17 4208316
    maciek_slon
    Poziom 29  
    Posty: 942
    Pomógł: 143
    Ocena: 27
    Warunek jest zły, powinien być tam >= 0 ponieważ z obecnym warunkiem nie da się odjąć obrazka od samego siebie bo zawsze mamy np. 200-200 = 0

    Jeśli można to poproszę jeden obrazek w oryginale (bmp) na mój e-mail:

    maciek.slon(_at_)poczta.fm
  • #18 4208444
    simba85
    Poziom 11  
    Posty: 12
    Ocena: 1
    Poszły wszystkie - wybierz sobie któryś ;-). Dzięki za pomoc!

    Dodano po 2 [minuty]:

    Po poprawieniu warunku mamy takie coś:
    Załączniki:
    • Jak odjąć jeden obraz .bmp od drugiego w C/C++ na Windows? Kopia (2) 515.1.jpg (76.32 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • #19 4208459
    simba85
    Poziom 11  
    Posty: 12
    Ocena: 1
    Czyli warunek jednak jest ważny - obrazek wygląda zupełnie inaczej niż bez warunku. Ale nie czaję dlaczego, bo jak wspomniałeś zawsze powinno wychodzić 0 (?).
  • #20 4208514
    maciek_slon
    Poziom 29  
    Posty: 942
    Pomógł: 143
    Ocena: 27
    Ok. Po przejrzeniu przysłąnych obrazków mam dobrą wiadomość - paleta w nich zapisana jest już uporządkowana więc z tym problemu nie będzie.

    Mam też drugą dorbą wiadomość - poprawiłem kod i działa tak jak powinien :P Przerobiłem algorytm tak żeby działał szybciej - nie wczytuje on po jednym pikselu ale od razu całą linię.

    
    long int pos;
    
    for (int y = 0; y<491; y++)
    		{
                pos = ftell(last);
                
                fseek(last, ftell(first), SEEK_SET);
                
                fread(line1, 1, 656, first);
                fread(line2, 1, 656, last);
                
                for (int x=0; x<656; x++)
    			{
    				if (line2[x] < line1[x])  line2[x] = 0; else
    				if (line2[x] == line1[x]) line2[x] = 0; else
    				                          line2[x] = line2[x] - line1[x];
    			}
                
                fseek(last, pos, SEEK_SET);
                
                fwrite(line2, 1, 656, last);
    		}
    
  • #21 4208634
    simba85
    Poziom 11  
    Posty: 12
    Ocena: 1
    Dziękuję :D. Sam chyba bym niewiele zdziałał :|
  • #22 4209302
    Sagaceil
    Poziom 11  
    Posty: 29
    Pomógł: 2
    Ocena: 1
    Nie wiem czy ktoś już pisał o tych przekształceniach pikseli. Więc załączam funkcję, których stosuje u siebie w programach :


    int tonuj(int c1, int c2, int j){
    unsigned char r1 = getr(c1), b1 = getb(c1), g1 = getg(c1);
    unsigned char r2 = getr(c2), b2 = getb(c2), g2 = getg(c2);
    return makecol(r1 - r1*j/255 + r2*j/255,g1 - g1*j/255 + g2*j/255,b1 - b1*j/255 + b2*j/255);
    }

    Legenda :
    * c1 oraz c2 to kolory bazowe, j współczynnik wymieszania barw od 0 do 255
    * getr, getg, oraz getb to wyłączanie kolorów składowych z barwy
    * makecol ( r, g, b) to budowanie koloru na nowo



    int cien(int col,float ilor){
    int C1 = getr(col), C2 = getg(col), C3 = getb(col);
    if(ilor >= 0)
    return makecol(C1-(int)((ilor*C1)/256),C2-(int)((ilor*C2)/256),C3-(int)((ilor*C3)/256));
    else
    return makecol(C1-(int)((ilor*(255-C1))/256),C2-(int)((ilor*(255-C2))/256),C3-(int)((ilor*(255-C3))/256));
    }

    Legenda:
    * col - kolor bazowy, ilor - stopień przyciemnienia od -255 do 255

    Poszażanie:

    Tutaj są dwie metody:
    * pierwsza, porostu zrobić średnia arytmetyczna kolorów składowych barwy
    czyli char i = (getr( col ) + getg( col ) + getb( col )) /3; po czym zrobić makecol( i, i, i );
    * lub zaszaleć i zrobić szarość uwzględniając czułość oka ludzkiego na poszczególne barwy składowe ( :p ) stosując w tym celu średnią ważoną:
    i = (0.299*getr( col ) + 0.587*getg( col ) + 0.114*getb( col )) makecol( i, i, i )

    Takie moje spostrzeżenia. Mam nadzieje, że komuś się przyda :P

Podsumowanie tematu

LABEL_AI_GENERATED
Dyskusja dotyczy implementacji odejmowania dwóch obrazów BMP w formacie 8-bitowego odcienia szarości o rozdzielczości 640x480 pikseli w języku C/C++ na systemie Windows, w tym Windows 98. Autor planuje wczytać obrazy, przejść po pikselach w pętli i odjąć wartości pikseli jednego obrazu od drugiego, a następnie zapisać wynik. Wskazano, że można użyć standardowych funkcji C, takich jak fread i fseek, do odczytu i manipulacji danymi pikselowymi bez konieczności stosowania zewnętrznych bibliotek, co może być szybsze i prostsze. Poruszono problem palety kolorów w 8-bitowych BMP, gdzie różne obrazy mogą mieć różne palety, co wymaga ich standaryzacji, jednak w przypadku obrazów z jednego źródła paleta powinna być identyczna i uporządkowana. Zaproponowano optymalizację odczytu poprzez wczytywanie całych linii pikseli zamiast pojedynczych pikseli. Omówiono także poprawność warunków logicznych przy odejmowaniu wartości pikseli, aby uniknąć błędów i uzyskać oczekiwany efekt (np. czarny obraz po odjęciu obrazu od samego siebie). Wskazano, że Windows API oferuje funkcje do obsługi obrazów BMP, a także podano przykładowe funkcje do manipulacji kolorami (tonowanie, przyciemnianie). Autor otrzymał pomoc w postaci kodu i wskazówek dotyczących formatów plików BMP oraz sposobu efektywnego przetwarzania danych obrazów.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA