2013-08-16 40 views
5

Po pierwsze chcę powiedzieć, że próbowałem wiele razy, aby znaleźć odpowiedź, używając wyszukiwarki Google, i znalazłem wiele wyników, ale nie zrozumiałem, ponieważ nie wiem, jaki jest pomysł czytania pliku binarnego i konwersji wartości, która Uzyskane do czytelnej wartości.Jak uzyskać szerokość/wysokość pliku jpeg bez korzystania z biblioteki?

Co próbowałem to zrobić.

unsigned char fbuff[16]; 
FILE *file; 
file = fopen("C:\\loser.jpg", "rb"); 
if(file != NULL){ 
    fseek(file, 0, SEEK_SET); 
    fread(fbuff, 1, 16, file); 
    printf("%d\n", fbuff[1]); 
    fclose(file); 
}else{ 
    printf("File does not exists."); 
} 

chcę proste wyjaśnienie z przykład pokazuje, jak uzyskać szerokość/wysokość pliku JPEG z jej nagłówka, a następnie przekonwertować tę wartość do wartości czytelny.

+0

Czy masz szczegóły tego, co jest zawarte w plikach JPEG? Jeśli tak, proszę dołącz to w swoim pytaniu. Wątpię, czy powyższa metoda zadziała, ponieważ na początku jest generalnie nagłówek, a następnie zaczynają się rzeczywiste wartości pikseli. Jeśli potrzebujesz tylko informacji o wysokości i szerokości, wierzę, że możesz to uzyskać, czytając sam nagłówek. – shrm

+0

@mishr: Mówię ogólnie o plikach jpeg. –

+0

Rozumiem to, ale pytanie brzmi: czy wiesz, jaki jest format plików jpeg? Czy chcesz, abyśmy znaleźli to dla ciebie? – shrm

Odpowiedz

12

Niestety, nie jest to proste dla JPEG. Powinieneś spojrzeć na źródło do narzędzia wiersza poleceń jhead. Dostarcza tych informacji. Podczas przechodzenia przez źródło zobaczysz funkcję ReadJpegSections. Ta funkcja skanuje wszystkie segmenty zawarte w pliku JPEG, aby wyodrębnić żądane informacje. Szerokość i wysokość obrazu jest uzyskiwana podczas przetwarzania ramek, które mają znacznik SOFn.

widzę źródło znajduje się w domenie publicznej, więc pokażę fragment, który pobiera informacje image:

static int Get16m(const void * Short) 
{ 
    return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; 
} 

static void process_SOFn (const uchar * Data, int marker) 
{ 
    int data_precision, num_components; 

    data_precision = Data[2]; 
    ImageInfo.Height = Get16m(Data+3); 
    ImageInfo.Width = Get16m(Data+5); 

z kodu źródłowego, to jest dla mnie jasne, nie ma jednego „nagłówek "z tymi informacjami. Musisz przeskanować plik JPEG, analizując każdy segment, aż znajdziesz segment zawierający informacje, które chcesz. Jest to opisane w wikipedia article:

JPEG obraz składa się z sekwencji segmentów, każdy początku z markerem, z których każda zaczyna się bajt 0xFF następnie bajt wskazujący, jaki rodzaj markera jest. Niektóre znaczniki składają się tylko z tych dwóch bajtów; po innych następują dwa bajty wskazujące długość danych właściwych dla danych właściwych dla markera.


Plik w formacie JPEG składa się z szeregu segmentów:

SEGMENT_0 
SEGMENT_1 
SEGMENT_2 
... 

Każdy segment zaczyna się znacznik 2 bajtów. Pierwszy bajt to 0xFF, drugi bajt określa typ segmentu. Następnie następuje kodowanie długości segmentu. W segmencie są dane specyficzne dla tego typu segmentu.

Szerokość i wysokość obrazu znajduje się w segmencie typu SOFn lub "Początek klatki [n]", gdzie "n" to pewna liczba, która oznacza coś specjalnego dla dekodera JPEG. Powinien być wystarczająco dobry, aby szukać tylko dla SOF0, a jego oznaczenie bajtu to 0xC0. Po znalezieniu tej ramki możesz ją odkodować, aby znaleźć wysokość i szerokość obrazu.

więc struktura programu, aby robić to, co chcesz wyglądałby następująco:

file_data = the data in the file 
data = &file_data[0] 
while (data not at end of file_data) 
    segment_type = decoded JPEG segment type at data 
    if (type != SOF0) 
     data += byte length for segment_type 
     continue 
    else 
     get image height and width from segment 
     return 

Jest to zasadniczo struktura znaleźć w Michael Petrov's get_jpeg_size() implementation.

+0

@LionKing, daj mi znać, jeśli wyjaśnienie nie jest jasne lub jeśli potrzebujesz dodatkowej pomocy. – jxh

+0

Dzięki, ale nie rozumiem tego, chcę bardzo prosty sposób i przykład, aby to zrozumieć. –

+0

Byłbym bardzo wdzięczny za przyczynę odrzucenia głosowania. Dzięki! – jxh

0

Oto prosty kod, który napisałem, który wydaje się działać niezawodnie.

#define MOTOSHORT(p) ((*(p))<<8) + *(p+1) 
unsigned char cBuf[32]; 
int iBytes, i, j, iMarker, iFilesize; 
unsigned char ucSubSample; 
int iBpp, iHeight, iWidth; 

     Seek(iHandle, 0, 0); // read the first 32 bytes 
     iBytes = Read(iHandle, cBuf, 32); 

     i = j = 2; /* Start at offset of first marker */ 
     iMarker = 0; /* Search for SOF (start of frame) marker */ 
     while (i < 32 && iMarker != 0xffc0 && j < iFileSize) 
      { 
      iMarker = MOTOSHORT(&cBuf[i]) & 0xfffc; 
      if (iMarker < 0xff00) // invalid marker, could be generated by "Arles Image Web Page Creator" or Accusoft 
       { 
       i += 2; 
       continue; // skip 2 bytes and try to resync 
       } 
      if (iMarker == 0xffc0) // the one we're looking for 
       break; 
      j += 2 + MOTOSHORT(&cBuf[i+2]); /* Skip to next marker */ 
      if (j < iFileSize) // need to read more 
       { 
       Seek(iHandle, j, 0); // read some more 
       iBytes = Read(iHandle, cBuf, 32); 
       i = 0; 
       } 
      else // error, abort 
       break; 
      } // while 
     if (iMarker != 0xffc0) 
      goto process_exit; // error - invalid file? 
     else 
      { 
      iBpp = cBuf[i+4]; // bits per sample 
      iHeight = MOTOSHORT(&cBuf[i+5]); 
      iWidth = MOTOSHORT(&cBuf[i+7]); 
      iBpp = iBpp * cBuf[i+9]; /* Bpp = number of components * bits per sample */ 
      ucSubSample = cBuf[i+11]; 
      } 
+0

Dzięki, jest poprzedni przykład przy użyciu 'C/C++' ?, co to jest 'Seek',' Read' funkcje?, I Co to jest korzyść z tej funkcji 'MOTOSHORT'?, Także co to jest zmienna' iHandle' ?. –

+0

Funkcje wyszukiwania i odczytu to ogólne pliki i/o, które powinny istnieć we wszystkich systemach. MOTOSHORT to makro (patrz górna część kodu), które jest wygodne do czytania dużych szortów endianów w każdym systemie, bez względu na jego koniec. Zmienna ihandle to uchwyt pliku, który zakłada się, że jest otwarty przed wywołaniem funkcji. – BitBank

0
int GetJpegDimensions(
    char   *pImage, 
    size_t   nSize, 
    unsigned32  *u32Width, 
    unsigned32  *u32Height, 
    char   *szErrMsg) 
{ 
    int    nIndex; 
    int    nStartOfFrame; 
    int    nError = NO_ERROR; 
    bool   markerFound = false; 
    unsigned char ucWord0; 
    unsigned char ucWord1; 

    // verify START OF IMAGE marker = FF D8 
    nIndex = 0; 
    ucWord0 = pImage[nIndex]; 
    ucWord1 = pImage[nIndex+1]; 

    // marker FF D8 starts a valid JPEG 
    if ((ucWord0 == 0xFF) && (ucWord1 == 0xD8)) 
    { 
     // search for START OF FRAME 0 marker FF C0 
     for (nIndex = 2; 
      (nIndex < nSize-2) && (markerFound == false); 
      nIndex += 2) 
     { 
      ucWord0 = pImage[nIndex]; 
      ucWord1 = pImage[nIndex+1]; 
      if (ucWord0 == 0xFF) 
      { 
       if (ucWord1 == 0xC0) 
       { 
        markerFound = true; 
        nStartOfFrame = nIndex; 
       } 
      } 
      if (ucWord1 == 0xFF) 
      { 
       ucWord0 = pImage[nIndex+2]; 
       if (ucWord0 == 0xC0) 
       { 
        markerFound = true; 
        nStartOfFrame = nIndex+1; 
       } 
      } 
     } // while 

     if (markerFound) 
     { 
      nError = NO_ERROR; 
      ucWord0 = pImage[nStartOfFrame+5]; 
      ucWord1 = pImage[nStartOfFrame+6]; 
      *u32Height = ucWord1 + (ucWord0 << 8); 

      ucWord0 = pImage[nStartOfFrame+7]; 
      ucWord1 = pImage[nStartOfFrame+8]; 
      *u32Width = ucWord1 + (ucWord0 << 8); 
     } 
     else 
     { 
      // start of frame 0 not found 
      nError = -2; 
      sprintf(szErrMsg, 
       "Not a valid JPEG image. START OF FRAME 0 marker FFC0 not found"); 
     } 
    } 
    else // START OF IMAGE marker not found 
    { 
     nError = -1; 
     sprintf(szErrMsg, 
      "Not a valid JPEG image. START OF IMAGE marker FFD8 not found"); 
    } 
    return nError; 
} 
4

następnie trzeba znaleźć wysokość i szerokość markerem JPEG, który jest [ffc0].

po znalezieniu ffc0 w formate binarnym, cztery, pięć bajtów to wysokość, a sześć i siedem bajtów to szerokość.

eg: [ff c0] d8 c3 c2 [ff da] [00 ff] 
         |   | 
         |   | 
         ->height ->width 

int position; 
unsigned char len_con[2]; 
/*Extract start of frame marker(FFC0) of width and hight and get the position*/ 
for(i=0;i<FILE_SIZE;i++) 
{ 
    if((image_buffer[i]==FF) && (image_buffer[i+1]==c0)) 
    { 
     position=i; 
    } 
} 
/*Moving to the particular byte position and assign byte value to pointer variable*/ 
position=position+5; 
*height=buffer_src[position]<<8|buffer_src[position+1]; 
*width=buffer_src[position+2]<<8|buffer_src[position+3]; 

printf("height %d",*height); 
printf("width %d",*width); 
0

Oto kod napisany w Javie. Działa dobrze dla jpegs pobranych z kamery. Skanuje cały kod, aby znaleźć największy rozmiar obrazu. Nie mogłem go poprawić, aby pominąć długości każdego bloku, ponieważ nie działa. Jeśli ktokolwiek może poprawić kod, aby to zrobić, byłoby wspaniale.

int getShort(byte[] p, int i) 
{ 
    int p0 = p[i] & 0xFF; 
    int p1 = p[i+1] & 0xFF; 
    return p1 | (p0 << 8); 
} 

int[] GetJpegDimensions(byte[] b) 
{ 
    int nIndex; 
    int height=0, width=0, size=0; 
    int nSize = b.length; 

    // marker FF D8 starts a valid JPEG 
    if (getShort(b,0) == 0xFFD8) 
     for (nIndex = 2; nIndex < nSize-1; nIndex += 4) 
      if (b[nIndex] == -1/*FF*/ && b[nIndex+1] == -64/*C0*/) 
      { 
      int w = getShort(b,nIndex+7); 
      int h = getShort(b,nIndex+5); 
      if (w*h > size) 
      { 
       size = w*h; 
       width = w; 
       height = h; 
      } 
      } 
    return new int[]{width,height}; 
} 
2

pytanie jest stare, a pozostałe odpowiedzi są poprawne, ale ich format nie jest najłatwiejszy. Ja po prostu użyć getc aby szybko uzyskać wymiary, a pomijanie nieistotnych markery (obsługuje również Progressive JPEG):

int height, width; 
    // start of image (SOI) 
    getc(f); // oxff 
    getc(f); // oxd8 
    // Scan miscellaneous markers until we reach SOF0 marker (0xC0) 
    for(;;) { 
    // next marker 
    int marker; 
    while((marker = getc(f)) != 0xFF); 
    while((marker = getc(f)) == 0xFF); 
    // SOF 
    if (marker == 0xC0 || marker == 0xC2) { 
     getc(f); // length (2 bytes) 
     getc(f); // # 
     getc(f); // bpp, usually 8 
     height = (getc(f) << 8) + getc(f); // height 
     width = (getc(f) << 8) + getc(f); // width 
     break; 
    } 
    } 
+0

Chyba że czegoś mi brakuje, ta i wszystkie inne odpowiedzi, które odczytują wszystkie bajty, zawiodą, jeśli segment cf lub cf c2 przyjdzie po jakimś innym segmencie, w którym ładunek zawiera ff c0/ff c2. –