JPEG(JFIF)画像のサイズを見つける必要があります。画像はスタンドアロンファイルとして保存されないため、GetFileSize
またはこのような他のAPIを使用できません(画像はストリームに配置され、通常のJPEGを除いて他のヘッダーは存在しません)/JFIFヘッダー)。
調べてみると、JPEG画像はフレームマーカー(0xFF 0xXX
)で始まる各部分と、このフレームのサイズで構成されていることがわかりました。この情報を使用して、ファイルから多くの情報を解析することができました。
問題は、圧縮データのフレームマーカーがないように見えるため、圧縮データのサイズが見つからないことです。また、圧縮されたデータはSOS(FFDA
)マーカーの後に続き、画像は画像の終わり(EOI)(FFD9
)マーカーで終わるようです。
これを実現する方法は、バイトごとにEOIマーカーを検索することですが、圧縮されたデータにはこのバイトの組み合わせが含まれている可能性があります。
画像の合計サイズを見つける簡単で正しい方法はありますか? (私はいくつかのコード/アイデアを好む外部ライブラリなしで)
基本的に、画像の開始(SOI -FFE0
)と画像の終了(EOI -FFD9
)の間の距離(バイト単位)が必要です。
圧縮データにはSOIまたはEOIバイトが含まれないため、安全です。ただし、コメント、アプリケーションデータ、またはその他のヘッダーが含まれる場合があります。幸い、これらのセクションを長さとして識別してスキップできます。与えられます。
JPEG仕様は、必要なものを示しています。
http://www.w3.org/Graphics/JPEG/itu-t81.pdf
32ページの表B.1を参照してください。*が付いているシンボルには、その後に長さフィールドがありません(RST、SOI、EOI、TEM)。他の人はそうします。
さまざまなフィールドをスキップする必要がありますが、それほど悪くはありません。
通過する方法:
読み取りを開始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を読み取ることからやり直すことができます。
多分このようなもの
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
投稿されている言語がないため、これが機能するかどうかはわかりませんが、次のようになります。
Stream.Seek(0, StreamOffset.End);
して、ストリームの位置を取得できますか?
使用しているフレームワークについて具体的に説明してください。
実際のところ、ファイルヘッダーで予想されるサイズが指定されていない場合は、画像の最後までシーク(または読み取り)する必要があります。
[〜#〜]編集[〜#〜]
複数のファイルをストリーミングしようとしているので、ストリーミングに適したコンテナ形式を使用することをお勧めします。
[〜#〜] ogg [〜#〜] これにぴったりです。
JPEGは実際にはすでにストリーミング対応ですが、ストリームに送信する前に、各ファイルに有効なターミネーターがあることを確認する必要があります。そうしないと、予期しない入力でアプリがクラッシュするリスクがあります。
Pythonでは、ファイル全体を文字列オブジェクトに読み込んで、FFE0の最初の出現とFFD9の最後の出現を見つけることができます。おそらく、これらはあなたが探している始まりと終わりですか?
f = open("filename.jpg", "r")
s = f.read()
start = s.find("\xff\xe0")
end = s.rfind("\xff\xd9")
imagesize = end - start