Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Odczyt i zmiana odcieni szarości piksela w C/C++

simba85 21 Sie 2007 16:54 6991 21
  • #1 21 Sie 2007 16:54
    simba85
    Poziom 9  

    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.

    0 21
  • #2 21 Sie 2007 17:55
    Arrow
    Poziom 12  

    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.

    0
  • Pomocny post
    #3 22 Sie 2007 00:34
    maciek_slon
    Poziom 29  

    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]:

    Code:

    #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

    0
  • #4 22 Sie 2007 08:35
    simba85
    Poziom 9  

    Dzięki, przetestuję dzisiaj Twoje rozwiązanie ;-)

    A co znaczą parametry rb i rb+?

    0
  • #6 22 Sie 2007 11:07
    Fyszo
    Spec od GSM

    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.

    0
  • #7 22 Sie 2007 11:58
    maciek_slon
    Poziom 29  

    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ć :-)

    0
  • #8 22 Sie 2007 17:50
    simba85
    Poziom 9  

    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ć ;-).

    0
  • #9 22 Sie 2007 20:07
    maciek_slon
    Poziom 29  

    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 :-) )

    0
  • #10 22 Sie 2007 23:54
    simba85
    Poziom 9  

    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.

    0
  • #11 23 Sie 2007 08:24
    maciek_slon
    Poziom 29  

    No mniej więcej o to chodzi, z pominięciem tego "najbardziej szarego" :P

    0
  • #12 23 Sie 2007 17:21
    simba85
    Poziom 9  

    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.

    0
  • #13 23 Sie 2007 17:22
    Fyszo
    Spec od GSM

    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.

    0
  • #14 23 Sie 2007 17:24
    simba85
    Poziom 9  

    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 ;-) ?

    0
  • #15 23 Sie 2007 17:25
    simba85
    Poziom 9  

    Fyszo, czy znasz jakąś bibliotekę z gotowymi takimi funkcjami? Byłbym wdzięczny ;-).

    0
  • #16 23 Sie 2007 17:40
    Fyszo
    Spec od GSM

    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Ę.

    0
  • #17 23 Sie 2007 18:32
    maciek_slon
    Poziom 29  

    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(malpa)poczta.fm

    0
  • #19 23 Sie 2007 19:08
    simba85
    Poziom 9  

    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 (?).

    0
  • #20 23 Sie 2007 19:25
    maciek_slon
    Poziom 29  

    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ę.

    Code:

    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);
          }

    0
  • #21 23 Sie 2007 20:03
    simba85
    Poziom 9  

    Dziękuję :D. Sam chyba bym niewiele zdziałał :|

    0
  • #22 23 Sie 2007 22:38
    Sagaceil
    Poziom 10  

    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

    0