[C#]PNGの幅,高さのみをファイルから高速に取得する方法について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
GRAM
記事: 164
登録日時: 13年前
住所: 大阪

[C#]PNGの幅,高さのみをファイルから高速に取得する方法について

#1

投稿記事 by GRAM » 8年前

こんにちは.いつもお世話になっております.
ご質問させていただきたいのですが,現在PNGで保存された4000枚ほどの画像を使って画像処理を行うアプリケーションを作っております.
処理の都合上4000枚の画像はすべて同じ幅,高さである必要があるのですが,以下の方法を用いると
与えられた4000枚の画像がすべて同じ幅,高さであることをチェックすることに非常に時間がかかります
(おそらくいちいちすべての画像をImageに読み込んでいるからだと思います.)

言語はC#
開発環境はVisual Studio 2015です.

コード:

private bool CheckSize(string[] files)
        {

            var img = System.Drawing.Image.FromFile(files[0]);
            int widthBase = img.Width;
            int heightBase = img.Height;
            img.Dispose();
            foreach (string name in files)
            {
                img = System.Drawing.Image.FromFile(name);
                int width = img.Width;
                int height = img.Height;
                img.Dispose();
                if (width != widthBase || height != heightBase)
                {
                    return false;
                }
            }
            ImageWidth = widthBase;
            ImageHeight = heightBase;

            return true;
        }
filesの中にはpngファイルのパスが含まれております.
この処理を高速に行うすべはないでしょうか?
(せめて数秒の内に終わってほしいのですが...)

以上です.よろしくお願いいたします.

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: [C#]PNGの幅,高さのみをファイルから高速に取得する方法について

#2

投稿記事 by みけCAT » 8年前

十分高速かどうかはわかりませんが、入力が有効なPNGしか来ないと仮定するなら、IHDRチャンクの幅と高さの情報を読めば画像の大きさが求まります。

コマンドラインの第一引数で指定されたファイルを読み込んで幅と高さを取得するサンプルです。(エラーチェックは省略しています)

コード:

using System;
using System.IO;

class GetPNGSize {
  static void Main(string[] args) {
    FileStream fs = new FileStream(args[0], FileMode.Open, FileAccess.Read);
    fs.Seek(16, SeekOrigin.Begin);
    byte[] buf = new byte[8];
    fs.Read(buf, 0, 8);
    fs.Dispose();
    uint width = ((uint)buf[0] << 24) | ((uint)buf[1] << 16) | ((uint)buf[2] << 8) | (uint)buf[3];
    uint height = ((uint)buf[4] << 24) | ((uint)buf[5] << 16) | ((uint)buf[6] << 8) | (uint)buf[7];
    Console.Write("width = " + width + ", height = " + height);
  }
}
参考
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
GRAM
記事: 164
登録日時: 13年前
住所: 大阪

Re: [C#]PNGの幅,高さのみをファイルから高速に取得する方法について

#3

投稿記事 by GRAM » 8年前

>>みけCATさん
返信ありがとうございます

それとごちゃってる部分はBitConverterで行けそうですね.
解決したらコードを載せておきます

<追記>
・・・と思ったらBitConverterってエンディアン指定できないんですね…

アバター
GRAM
記事: 164
登録日時: 13年前
住所: 大阪

Re: [C#]PNGの幅,高さのみをファイルから高速に取得する方法について

#4

投稿記事 by GRAM » 8年前

一応こうしました
ありがとうございました

コード:

        private bool CheckSize(string[] files)
        {

            int widthBase;
            int heightBase;
            LoadPNGSize(files[0], out widthBase, out heightBase);

            foreach (string name in files)
            {
                int width;
                int height;

                LoadPNGSize(name, out width, out height);

                if (width != widthBase || height != heightBase)
                {
                    return false;
                }
            }
            ImageWidth = widthBase;
            ImageHeight = heightBase;

            return true;
        }

        void LoadPNGSize(string name, out int width, out int height)
        {
            FileStream fs = new FileStream(name, FileMode.Open, FileAccess.Read);
            fs.Seek(16, SeekOrigin.Begin);
            byte[] buf = new byte[8];
            fs.Read(buf, 0, 8);
            fs.Dispose();
            

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(buf);
                width = BitConverter.ToInt32(buf, 4);
                height = BitConverter.ToInt32(buf, 0);
            }
            else
            {
                width = BitConverter.ToInt32(buf, 0);
                height = BitConverter.ToInt32(buf, 4);
            }
        }

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: [C#]PNGの幅,高さのみをファイルから高速に取得する方法について

#5

投稿記事 by YuO » 8年前

ちょっと気になったので。
GRAM さんが書きました:一応こうしました
Disposeを直接呼ぶことがあった場合,原則としてusingを使ってください。
LoadPNGSizeメソッドの先頭部分は,こう書けます。

コード:

            byte[] buf;
            using (var fs = File.OpenRead(name))
            {
                fs.Seek(16, SeekOrigin.Begin);
                buf = new byte[8];
                fs.Read(buf, 0, 8);
            }
これは,以下のコードに展開されます。

コード:

            byte[] buf;
            var fs = File.OpenRead(name);
            try
            {
                fs.Seek(16, SeekOrigin.Begin);
                buf = new byte[8];
                fs.Read(buf, 0, 8);
            }
            finally
            {
                if (fs != null)
                {
                    ((IDisposable)fs).Dispose();
                }
            }
まぁ,そうそうusingのブロック中で例外が発生するコードではないですが,だいたいのリソースはusingに閉じ込めるのが処理として楽です。
例外とは関係なく,途中でreturnしてもちゃんとDisposeされる,という利点も生まれますし。
ただ,varによる推論があった場合にusingの外まで持っていくためにvarを使えない,というのが難点にはなります。
# 今回のbufがその例。
オフトピック
GRAM さんが書きました:・・・と思ったらBitConverterってエンディアン指定できないんですね…
ビットシフトを自分で書いた方が確実に速いと思いますが,Take, Skip, Reverse, ToArray各拡張メソッドを使うことで,BitConverter.GetInt32等による変換ができます。

アバター
GRAM
記事: 164
登録日時: 13年前
住所: 大阪

Re: [C#]PNGの幅,高さのみをファイルから高速に取得する方法について

#6

投稿記事 by GRAM » 8年前

>YuOさん
ありがとうございます.
Disposeの件はたしかにそうですね.
ありがとうございました.
上記のコード(using部は修正しました)では数秒で開くので目的は達成しました.
皆さんありがとうございました.

閉鎖

“C言語何でも質問掲示板” へ戻る