pngファイルのサイズをBitmapクラスを経由せずに知る方法

タグの編集
投稿者 バーバル  (社会人) 投稿日時 2020/9/1 18:01:22
こんにちは!
今VBで画像処理の勉強をしています。

さて、質問ですが、VBを使用して画像ファイル(とりあえずはpng形式)のサイズを知りたい場合、
この画像ファイルのBitmapクラスを生成すれば、このSizeプロパティで確認できます。

ただ、1辺が4000ピクセルもある画像ファイルがたくさんある場合、
Bitmapへの読み出しはそれなりの負荷がかかりそうで、ベストな方法なのかどうか分かりません。
他にもっと軽い処理でpngファイルのサイズを知る方法ってありますか?
それとも、Bitmapクラスに読み込む方法が正当な方法でしょうか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/9/1 23:52:17
ここでいうサイズというのは、ファイルサイズのことではなく、
縦横のピクセル数のことで良いのですよね。

多少手間がかかっても良いのなら、画像のバイナリを直接解析することで
巨大なファイルであっても、最低限のメモリ消費量で幅と高さを得ることが出来ます。

Public Overloads Function GetPngSize(png As StringAs Size
    Using fs As New FileStream(png, FileMode.Open)
        Dim bin(33) As Byte
        fs.Read(bin, 0, 33)
        Return GetPngSize(bin)
    End Using
End Function
Public Overloads Function GetPngSize(png As IEnumerable(Of Byte)) As Size
    Dim bin As Byte() = png.Take(33).ToArray()
    If BitConverter.ToUInt64(bin, 0) <> &HA1A0A0D474E5089UL Then
        Throw New BadImageFormatException("PNG画像ではありません。")
    ElseIf BitConverter.ToUInt64(bin, 8) <> &H524448490D000000UL Then
        Throw New NotSupportedException("IHDR チャンクが存在しません。")
    End If
    Dim width As Integer = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(bin, 16))
    Dim height As Integer = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(bin, 20))
    'Dim bitDepth As Byte = bin(24) 
    'Dim colorType As Byte = bin(25) 
    'Dim compressionMethod As Byte = bin(26) 
    'Dim filterMethod As Byte = bin(27) 
    'Dim interlaceMethod As Byte = bin(28) 
    'Dim CRC As UInteger = BitConverter.ToUInt32(bin, 29) 
    Return New Size(width, height)
End Function
投稿者 バーバル  (社会人) 投稿日時 2020/9/2 11:29:51
おお!ありがとうございます。

>ここでいうサイズというのは、ファイルサイズのことではなく、
>縦横のピクセル数のことで良いのですよね。

正にその通りです。
これを試してみたいと思います。
ありがとうございました。
投稿者 バーバル  (社会人) 投稿日時 2020/9/2 11:44:30
一応、本題は解決したのですが、今回のサンプルの文法で質問があります。
GetPngSizeというメソッドについて、引数の内容(シグネチャっていうんでしょうか)が異なるため、
「Overloads」という文字がついているのだと思いますが、この前、引数の内容の異なる同じ名前のメソッドに「Overloads」をつけなかったのですが問題なく動きました。
この「Overloads」は、省略可だけどオーバーロードであることを明示したい場合につけるのでしょうか、それとも「Overloads」をつけなければならないケースがあるのでしょうか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/9/4 00:11:16
> この「Overloads」は、省略可だけどオーバーロードであることを明示したい場合に
> つけるのでしょうか、それとも「Overloads」をつけなければならないケースがあるのでしょうか?

答えとしては Yes です。

省略して問題ないケースもあります。(今回は省略可能です)
付与しなければならないケースもあります。
省略できるが付与すべきなケースもあります。


たとえば先の GetPngSize の場合、一方だけに Overloads を付与し、
他方は Overloads を記載しなかった場合、これは
コンパイルエラー BC31409 を誘発します。


エラーにはならないけれども、付与すべきという例も挙げておきましょう。

Public Class Form1
    Inherits System.Windows.Forms.Form

    Public Sub InitLayout(dgv As DataGridView)

    End Sub
End Class


上記の Public メソッドは、
「Sub InitLayout」ではなく、
「Overloads Sub InitLayout」とすべきです。

これは、継承元のクラスに、Protected な
「Overridable Sub InitLayout」が存在するためです。


継承元のクラスのいずれかに同名のメンバーが存在し、
それが MustOverride または Overridable である場合には、
たとえ引数定義が異なっていても、Overloads を付けるべきとされています。
省略は可能なのですが、既定のコンパイラ設定だと警告されます。

また、継承元と継承先とで、名前も引数定義も完全に同じ場合には、
Shadows / Overrides のいずれかを明示します。
省略した場合は Shadows 扱いですが、省略すると警告対象です。
投稿者 バーバル  (社会人) 投稿日時 2020/9/4 09:28:15
おお!詳細なご説明ありがとうございました!
Overloadsは省略できない場合がある(すでに同名でOverloadsがついている場合)ことを考えると、
「このメソッドにはオーバーロードがあるよー」ということを宣言する意味で、
Overloadsを積極的につけた方がいいかも知れませんね。
(もちろん、開発時に守るべきコード規約があれば話は別ですけれど。)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/9/4 12:20:17
> Overloadsを積極的につけた方がいいかも知れませんね。
> (もちろん、開発時に守るべきコード規約があれば話は別ですけれど。)

蛇足ついでに述べておくと、あえて Overloads 無しで記載することが求められるケースもあります。

Form や 型付 DataSet などのように、デザイン時にコードジェネレーターによって
Partial Class が生成されるようなケースです。

こうしたデザイナコードのメソッドには通常、Overloads が付与されていませんので、
それらをオーバーロードする場合には、意図的に「Overloads を記載しない」ことになります。

わざわざ *.designer.vb 側に Overloads を記載してまわるわけにも行きませんからね。
投稿者 バーバル  (社会人) 投稿日時 2020/9/7 16:02:38
追加情報、ありがとうございます!
今後ともよろしくお願い致します。