web-dev-qa-db-ja.com

JPEG(JFIF)画像のサイズの決定

JPEG(JFIF)画像のサイズを見つける必要があります。画像はスタンドアロンファイルとして保存されないため、GetFileSizeまたはこのような他のAPIを使用できません(画像はストリームに配置され、通常のJPEGを除いて他のヘッダーは存在しません)/JFIFヘッダー)。

調べてみると、JPEG画像はフレームマーカー(0xFF 0xXX)で始まる各部分と、このフレームのサイズで構成されていることがわかりました。この情報を使用して、ファイルから多くの情報を解析することができました。

問題は、圧縮データのフレームマーカーがないように見えるため、圧縮データのサイズが見つからないことです。また、圧縮されたデータはSOS(FFDA)マーカーの後に続き、画像は画像の終わり(EOI)(FFD9)マーカーで終わるようです。

これを実現する方法は、バイトごとにEOIマーカーを検索することですが、圧縮されたデータにはこのバイトの組み合わせが含まれている可能性があります。

画像の合計サイズを見つける簡単で正しい方法はありますか? (私はいくつかのコード/アイデアを好む外部ライブラリなしで

基本的に、画像の開始(SOI -FFE0)と画像の終了(EOI -FFD9)の間の距離(バイト単位)が必要です。

30
botismarius

圧縮データにはSOIまたはEOIバイトが含まれないため、安全です。ただし、コメント、アプリケーションデータ、またはその他のヘッダーが含まれる場合があります。幸い、これらのセクションを長さとして識別してスキップできます。与えられます。

JPEG仕様は、必要なものを示しています。
http://www.w3.org/Graphics/JPEG/itu-t81.pdf

32ページの表B.1を参照してください。*が付いているシンボルには、その後に長さフィールドがありません(RST、SOI、EOI、TEM)。他の人はそうします。

さまざまなフィールドをスキップする必要がありますが、それほど悪くはありません。

通過する方法:

  1. 読み取りを開始SOI(FFD8)。これが開始です。ストリームの最初のものである必要があります。

    • 次に、ファイルを進めて、さらにマーカーを見つけ、ヘッダーをスキップします。

    • SOIマーカー(FFD8):破損した画像。あなたはすでにEOIを見つけているはずです!

    • TEM(FF01):スタンドアロンマーカー、続行します。

    • RST(FFD0からFFD7):スタンドアロンマーカー、続行します。再起動マーカーがFFD0からFFD7までカウントアップして繰り返すことを検証できますが、長さの測定には必要ありません。

    • EOIマーカー(FFD9):これで完了です。

    • RST、SOI、EOI、TEM以外のマーカー(FF01からFFFE、上記の例外を除く):マーカーの後、次の2バイトを読み取ります。これは、16ビットのビッグです-そのフレームヘッダーのエンディアン長(2バイトマーカーは含まれませんが、長さフィールドは含まれます)。指定された量をスキップします(これらのバイトはすでに取得されているため、通常は長さから2を引いたものです)。

    • EOIの前にファイルの終わりを取得した場合は、イメージが破損しています。

    • EOIを取得したら、JPEGを取得したので、長さが必要です。ストリームに複数のJPEGが含まれると予想される場合は、別のSOIを読み取ることからやり直すことができます。

38
Adam Goode

多分このようなもの

int GetJpgSize(unsigned char *pData, DWORD FileSizeLow, unsigned short *pWidth, unsigned short *pHeight)
{
  unsigned int i = 0;


  if ((pData[i] == 0xFF) && (pData[i + 1] == 0xD8) && (pData[i + 2] == 0xFF) && (pData[i + 3] == 0xE0)) {
    i += 4;

    // Check for valid JPEG header (null terminated JFIF)
    if ((pData[i + 2] == 'J') && (pData[i + 3] == 'F') && (pData[i + 4] == 'I') && (pData[i + 5] == 'F')
        && (pData[i + 6] == 0x00)) {

      //Retrieve the block length of the first block since the first block will not contain the size of file
      unsigned short block_length = pData[i] * 256 + pData[i + 1];

      while (i < FileSizeLow) {
        //Increase the file index to get to the next block
        i += block_length; 

        if (i >= FileSizeLow) {
          //Check to protect against segmentation faults
          return -1;
        }

        if (pData[i] != 0xFF) {
          return -2;
        } 

        if (pData[i + 1] == 0xC0) {
          //0xFFC0 is the "Start of frame" marker which contains the file size
          //The structure of the 0xFFC0 block is quite simple [0xFFC0][ushort length][uchar precision][ushort x][ushort y]
          *pHeight = pData[i + 5] * 256 + pData[i + 6];
          *pWidth = pData[i + 7] * 256 + pData[i + 8];

          return 0;
        }
        else {
          i += 2; //Skip the block marker

          //Go to the next block
          block_length = pData[i] * 256 + pData[i + 1];
        }
      }

      //If this point is reached then no size was found
      return -3;
    }
    else {
      return -4;
    } //Not a valid JFIF string
  }
  else {
    return -5;
  } //Not a valid SOI header

  return -6;
}  // GetJpgSize
2
user243783

投稿されている言語がないため、これが機能するかどうかはわかりませんが、次のようになります。

Stream.Seek(0, StreamOffset.End);して、ストリームの位置を取得できますか?

使用しているフレームワークについて具体的に説明してください。

実際のところ、ファイルヘッダーで予想されるサイズが指定されていない場合は、画像の最後までシーク(または読み取り)する必要があります。

[〜#〜]編集[〜#〜]

複数のファイルをストリーミングしようとしているので、ストリーミングに適したコンテナ形式を使用することをお勧めします。

[〜#〜] ogg [〜#〜] これにぴったりです。

JPEGは実際にはすでにストリーミング対応ですが、ストリームに送信する前に、各ファイルに有効なターミネーターがあることを確認する必要があります。そうしないと、予期しない入力でアプリがクラッシュするリスクがあります。

2
John Gietzen

Pythonでは、ファイル全体を文字列オブジェクトに読み込んで、FFE0の最初の出現とFFD9の最後の出現を見つけることができます。おそらく、これらはあなたが探している始まりと終わりですか?

f = open("filename.jpg", "r")
s = f.read()
start = s.find("\xff\xe0")
end = s.rfind("\xff\xd9")
imagesize = end - start
0
Chinmay Kanchi