54

ファイルから画像を読み込んでいますが、ファイルから完全に読み取る前に画像を検証する方法を知りたいです。

string filePath = "image.jpg";
Image newImage = Image.FromFile(filePath);

この問題は、image.jpgが実際にはjpgではない場合に発生します。たとえば、空のテキストファイルを作成してその名前をimage.jpgに変更すると、image.jpgが読み込まれたときにOutOfMemory例外がスローされます。

画像のストリームまたはファイルパスを指定して画像を検証する関数を探しています。

関数プロトタイプの例

bool IsValidImage(string fileName);
bool IsValidImage(Stream imageStream);
4

14 に答える 14

84

これが私の画像チェックです。ファイル拡張子に頼ることができず、自分でフォーマットを確認する必要があります。バイト配列から WPF に BitmapImages を読み込んでいますが、事前にフォーマットがわかりません。WPF は形式を適切に検出しますが、BitmapImage オブジェクトの画像形式を通知しません (少なくとも、このプロパティについては知りません)。そして、フォーマットを検出するためだけに System.Drawing で画像を再度ロードしたくありません。このソリューションは高速で、私にとってはうまく機能します。

public enum ImageFormat
{
    bmp,
    jpeg,
    gif,
    tiff,
    png,
    unknown
}

public static ImageFormat GetImageFormat(byte[] bytes)
{
    // see http://www.mikekunz.com/image_file_header.html  
    var bmp    = Encoding.ASCII.GetBytes("BM");     // BMP
    var gif    = Encoding.ASCII.GetBytes("GIF");    // GIF
    var png    = new byte[] { 137, 80, 78, 71 };    // PNG
    var tiff   = new byte[] { 73, 73, 42 };         // TIFF
    var tiff2  = new byte[] { 77, 77, 42 };         // TIFF
    var jpeg   = new byte[] { 255, 216, 255, 224 }; // jpeg
    var jpeg2  = new byte[] { 255, 216, 255, 225 }; // jpeg canon

    if (bmp.SequenceEqual(bytes.Take(bmp.Length)))
        return ImageFormat.bmp;

    if (gif.SequenceEqual(bytes.Take(gif.Length)))
        return ImageFormat.gif;

    if (png.SequenceEqual(bytes.Take(png.Length)))
        return ImageFormat.png;

    if (tiff.SequenceEqual(bytes.Take(tiff.Length)))
        return ImageFormat.tiff;

    if (tiff2.SequenceEqual(bytes.Take(tiff2.Length)))
        return ImageFormat.tiff;

    if (jpeg.SequenceEqual(bytes.Take(jpeg.Length)))
        return ImageFormat.jpeg;

    if (jpeg2.SequenceEqual(bytes.Take(jpeg2.Length)))
        return ImageFormat.jpeg;

    return ImageFormat.unknown;
}
于 2012-02-25T16:59:19.643 に答える
33

Windowsフォームの使用:

bool IsValidImage(string filename)
{
    try
    {
        using(Image newImage = Image.FromFile(filename))
        {}
    }
    catch (OutOfMemoryException ex)
    {
        //The file does not have a valid image format.
        //-or- GDI+ does not support the pixel format of the file

        return false;
    }
    return true;
}

それ以外の場合、WPFを使用している場合は、次の操作を実行できます。

bool IsValidImage(string filename)
{
    try
    {
        using(BitmapImage newImage = new BitmapImage(filename))
        {}
    }
    catch(NotSupportedException)
    {
        // System.NotSupportedException:
        // No imaging component suitable to complete this operation was found.
        return false;
    }
    return true;
}

作成したイメージを解放する必要があります。そうしないと、この関数を何度も呼び出すと、システムがリソースを使い果たしたためにOutOfMemoryExceptionがスローされます。画像が破損して誤った結果が生成されたためではなく、この手順の後に画像を削除すると、削除される可能性があります。良いもの。

于 2008-10-16T23:43:28.047 に答える
23

JPEGには正式なヘッダー定義はありませんが、使用できるメタデータが少量あります。

  • オフセット0(2バイト):JPEG SOIマーカー(FFD8 hex)
  • オフセット2(2バイト):画像の幅(ピクセル単位)
  • オフセット4(2バイト):画像の高さ(ピクセル単位)
  • オフセット6(バイト):コンポーネントの数(1 =グレースケール、3 = RGB)

その後、他にもいくつかありますが、それらは重要ではありません。

バイナリストリームを使用してファイルを開き、この初期データを読み取り、オフセット0が0であり、オフセット6が1、2、または3であることを確認できます。

それは少なくともあなたにわずかにより高い精度を与えるでしょう。

または、例外をトラップして先に進むこともできますが、チャレンジが必要だと思いました:)

于 2008-10-16T23:42:38.983 に答える
20

さて、私は先に進んで、問題を解決するための一連の関数をコーディングしました。最初にヘッダーをチェックしてから、try/catch ブロックにイメージをロードしようとします。GIF、BMP、JPG、および PNG ファイルのみをチェックします。imageHeaders にヘッダーを追加することで、簡単にタイプを追加できます。

static bool IsValidImage(string filePath)
{
    return File.Exists(filePath) && IsValidImage(new FileStream(filePath, FileMode.Open, FileAccess.Read));
}

static bool IsValidImage(Stream imageStream)
{
    if(imageStream.Length > 0)
    {
        byte[] header = new byte[4]; // Change size if needed.
        string[] imageHeaders = new[]{
                "\xFF\xD8", // JPEG
                "BM",       // BMP
                "GIF",      // GIF
                Encoding.ASCII.GetString(new byte[]{137, 80, 78, 71})}; // PNG

        imageStream.Read(header, 0, header.Length);

        bool isImageHeader = imageHeaders.Count(str => Encoding.ASCII.GetString(header).StartsWith(str)) > 0;
        if (isImageHeader == true)
        {
            try
            {
                Image.FromStream(imageStream).Dispose();
                imageStream.Close();
                return true;
            }

            catch
            {

            }
        }
    }

    imageStream.Close();
    return false;
}
于 2008-10-17T05:23:23.757 に答える
14

ヘッダーをスニッフィングすることで、大まかな入力を行うことができます。

これは、実装する各ファイル形式に識別可能なヘッダーが必要であることを意味します...

JPEG:最初の4バイトはFF D8 FF E0です(実際には、最初の2バイトだけがjfif以外のjpegに対してそれを行います。詳細はこちら)。

GIF:最初の6バイトは「GIF87a」または「GIF89a」のいずれかです(詳細はこちら

PNG:最初の8バイトは次のとおりです:89 50 4E 47 0D 0A 1A 0A(詳細はこちら

TIFF:最初の4バイトは次のとおりです:II42またはMM42(詳細はこちら

など...気になるほぼすべてのグラフィック形式のヘッダー/形式情報を検索し、必要に応じて処理するものに追加できます。これでは、ファイルがそのタイプの有効なバージョンであるかどうかがわかりますが、「画像ではなく画像」についてのヒントが得られます。それでも画像が破損しているか不完全である可能性があり、開くとクラッシュする可能性があるため、.FromFile呼び出しをキャッチする必要があります。

于 2008-10-16T23:46:55.637 に答える
6

これでうまくいくはずです-ヘッダーから生のバイトを読み取る必要はありません:

using(Image test = Image.FromFile(filePath))
{
    bool isJpeg = (test.RawFormat.Equals(ImageFormat.Jpeg));
}

もちろん、OutOfMemoryException もトラップする必要があります。これにより、ファイルがまったく画像でない場合に節約できます。

また、ImageFormat には、GDI+ がサポートする他のすべての主要なイメージ タイプの項目があらかじめ設定されています。

演算子 == は Equals メソッドを呼び出すためにオーバーロードされていないため、ImageFormat オブジェクト (列挙型ではない) では == ではなく .Equals() を使用する必要があります。

于 2010-06-29T19:53:24.773 に答える
3

上記のすべての機能にいくつかの問題があることに気づきました。まず第一に - Image.FromFile は指定された画像を開き、その後、何らかの理由で指定された画像ファイルを開こうとすると、ファイルを開くエラーが発生します。アプリケーション自体も - Image.FromStream を使用して切り替えました。

API を切り替えた後 - 例外の種類が OutOfMemoryException から ArgumentException に変更されました。理由は不明です。(おそらく .net フレームワークのバグ?)

また、.net が現在よりも多くの画像ファイル形式のサポートを追加する場合は、関数ごとにチェックします。最初に画像を読み込んで失敗した場合にのみ試して、その後でエラーを報告するのが理にかなっています。

したがって、私のコードは次のようになります。

try {
    using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
    {
        Image im = Image.FromStream(stream);
        // Do something with image if needed.
    }
}
catch (ArgumentException)
{
    if( !IsValidImageFormat(path) )
        return SetLastError("File '" + fileName + "' is not a valid image");

    throw;
}

どこ:

/// <summary>
/// Check if we have valid Image file format.
/// </summary>
/// <param name="path"></param>
/// <returns>true if it's image file</returns>
public static bool IsValidImageFormat( String path )
{
    using ( FileStream fs = File.OpenRead(path) )
    {
        byte[] header = new byte[10];
        fs.Read(header, 0, 10);

        foreach ( var pattern in new byte[][] {
                    Encoding.ASCII.GetBytes("BM"),
                    Encoding.ASCII.GetBytes("GIF"),
                    new byte[] { 137, 80, 78, 71 },     // PNG
                    new byte[] { 73, 73, 42 },          // TIFF
                    new byte[] { 77, 77, 42 },          // TIFF
                    new byte[] { 255, 216, 255, 224 },  // jpeg
                    new byte[] { 255, 216, 255, 225 }   // jpeg canon
            } )
        {
            if (pattern.SequenceEqual(header.Take(pattern.Length)))
                return true;
        }
    }

    return false;
} //IsValidImageFormat
于 2015-12-04T11:33:30.973 に答える
3

TiffやJPEGにも対応した方式

private bool IsValidImage(string filename)
{
    Stream imageStream = null;
    try
    {
        imageStream = new FileStream(filename, FileMode.Open);

        if (imageStream.Length > 0)
        {
            byte[] header = new byte[30]; // Change size if needed.
            string[] imageHeaders = new[]
            {
                "BM",       // BMP
                "GIF",      // GIF
                Encoding.ASCII.GetString(new byte[]{137, 80, 78, 71}),// PNG
                "MM\x00\x2a", // TIFF
                "II\x2a\x00" // TIFF
            };

            imageStream.Read(header, 0, header.Length);

            bool isImageHeader = imageHeaders.Count(str => Encoding.ASCII.GetString(header).StartsWith(str)) > 0;
            if (imageStream != null)
            {
                imageStream.Close();
                imageStream.Dispose();
                imageStream = null;
            }

            if (isImageHeader == false)
            {
                //Verify if is jpeg
                using (BinaryReader br = new BinaryReader(File.Open(filename, FileMode.Open)))
                {
                    UInt16 soi = br.ReadUInt16();  // Start of Image (SOI) marker (FFD8)
                    UInt16 jfif = br.ReadUInt16(); // JFIF marker

                    return soi == 0xd8ff && (jfif == 0xe0ff || jfif == 57855);
                }
            }

            return isImageHeader;
        }

        return false;
    }
    catch { return false; }
    finally
    {
        if (imageStream != null)
        {
            imageStream.Close();
            imageStream.Dispose();
        }
    }
}
于 2010-03-11T13:00:34.030 に答える
1

私はセミコロンの答えを取り、VBに変換しました:

Private Function IsValidImage(imageStream As System.IO.Stream) As Boolean

            If (imageStream.Length = 0) Then
                isvalidimage = False
                Exit Function
            End If

            Dim pngByte() As Byte = New Byte() {137, 80, 78, 71}
            Dim pngHeader As String = System.Text.Encoding.ASCII.GetString(pngByte)

            Dim jpgByte() As Byte = New Byte() {255, 216}
            Dim jpgHeader As String = System.Text.Encoding.ASCII.GetString(jpgByte)

            Dim bmpHeader As String = "BM"
            Dim gifHeader As String = "GIF"

            Dim header(3) As Byte

            Dim imageHeaders As String() = New String() {jpgHeader, bmpHeader, gifHeader, pngHeader}
            imageStream.Read(header, 0, header.Length)

            Dim isImageHeader As Boolean = imageHeaders.Count(Function(str) System.Text.Encoding.ASCII.GetString(header).StartsWith(str)) > 0

            If (isImageHeader) Then
                Try
                    System.Drawing.Image.FromStream(imageStream).Dispose()
                    imageStream.Close()
                    IsValidImage = True
                    Exit Function
                Catch ex As Exception
                    System.Diagnostics.Debug.WriteLine("Not an image")
                End Try
            Else
                System.Diagnostics.Debug.WriteLine("Not an image")
            End If

            imageStream.Close()
            IsValidImage = False
        End Function
于 2012-02-09T17:37:15.523 に答える
0

次のようなメソッドを作成します。

Image openImage(string filename);

例外を処理します。戻り値がNullの場合、無効なファイル名/タイプがあります。

于 2008-10-16T23:38:52.813 に答える
0

ストリームの最初の数バイトを読み取り、それらをJPEGのマジックヘッダーバイトと比較することができます。

于 2008-10-16T23:43:24.177 に答える
-1

後で他の操作や他のファイルタイプ (PSD など) のためにそのデータを読み取る必要がある場合、Image.FromStream関数を使用することは必ずしも良い考えではありません。

于 2010-01-14T11:17:48.567 に答える