SelectActiveFrameについて

タグの編集
投稿者 allgreen  (社会人) 投稿日時 2020/5/1 05:03:59
Imports System.Drawing.Imaging
Public Class Form1
    Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
        Dim マルチ画像 As Image
        Dim ページ数 As Integer
        Dim i As Integer
        Dim 分割画像() As Image

        マルチ画像 = Image.FromFile("マルチ.tif")
        Dim fd As New FrameDimension(マルチ画像.FrameDimensionsList(0))
        ページ数 = マルチ画像.GetFrameCount(fd)
        ReDim 分割画像(ページ数 - 1)
        For i = 0 To ページ数 - 1
            画像.SelectActiveFrame(fd, i)
            分割画像(i) = 画像
        Next
    End Sub
End Class

このプログラムですが
マルチのtifファイルを読み込んで、ページ数を取得
1ぺーじごとに分割画像という配列に代入するのですが
思うような結果になりません

3ページのファイルの場合
0ページの画像が、分割画像(0)に
1ページの画像が、分割画像(1)に
2ページの画像が、分割画像(2)に
ところが結果は、分割画像(0)も(1)も(2)もすべて、2ページの画像になります
pictureboxを複数並べて、そこに表示させようとしても同じでした
ただ、pictureboxの場合は、doeventsなど待ち時間を作ってやれば正しく表示されますが
配列変数に代入するときは、待ち時間を作ってもだめでした
動作確認などいろいろやってみてるのですが
一旦は、0ページの画像が、分割画像(0)に書き込まれてます
step を2にして
分割画像(0) (2) (4) としたときは、書き込んだところだけ
すべて最後に書き込んだ画像になりました

このようになる原因が何なのかと対策方法はありますでしょうか?

よろしくおねがいします
投稿者 葉月  (社会人) 投稿日時 2020/5/1 10:26:55
初めまして。
SelectActiveFrameメソッドを知らなかったのですが、MSDNを読んだらその動きが正しいようです。
前のフレームに加えられた全ての変更が破棄されます。

>参考URL
https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.image.selectactiveframe?view=dotnet-plat-ext-3.1

DobonさんにBitmapDecoderを使った例がありました。
一度、ここのサンプルを試し、改良で要望を満たせないかご確認ください。
詳しくはdobon BitmapDecoder マルチTIFFでググってください。
投稿者 allgreen  (社会人) 投稿日時 2020/5/1 16:07:03
早速のご返事ありがとうございます
確かにMSDNに「前のフレームに加えられたすべての変更が破棄されます」とありますが
フレームに変更を加えたのではなく、フレームから取り出したものまで破棄されてしまう?
ここが理解できないところですが、仕方がないのでしょうね

いろいろと試したものの結果ですが
その1
  画像.SelectActiveFrame(fd, 0)
  picturebox1.image=画像
  画像.SelectActiveFrame(fd, 1)
  picturebox2.image=画像
  画像.SelectActiveFrame(fd, 2)
  picturebox3.image=画像
    結果は、pictureboxの3つとも2ページ目の画像
その2
  画像.SelectActiveFrame(fd, 0)
  picturebox1.image=画像
  System.Windows.Forms.Application.DoEvents()
  画像.SelectActiveFrame(fd, 1)
  picturebox2.image=画像
  System.Windows.Forms.Application.DoEvents()
  画像.SelectActiveFrame(fd, 2)
  picturebox3.image=画像
  System.Windows.Forms.Application.DoEvents()
    結果は、picturebox1は0ページ
          picturebox2は1ページ
                     picturebox3は2ページの画像
その3
  画像.SelectActiveFrame(fd, 0)
  分割画像(0)=画像
  System.Windows.Forms.Application.DoEvents()
  画像.SelectActiveFrame(fd, 1)
  分割画像(1)=画像
  System.Windows.Forms.Application.DoEvents()
  画像.SelectActiveFrame(fd, 2)
  分割画像(2)=画像
  System.Windows.Forms.Application.DoEvents()
    picturebox1.image=分割画像(0)
    picturebox2.image=分割画像(1)
    picturebox3.image=分割画像(2)
    結果は、pictureboxの3つとも2ページ目の画像

破棄されるのなら、なぜ「その2」は破棄されずに、こちらが思う結果になるのか

とりあえず、今回は
毎回  画像 = Image.FromFile(.FileName) からやり直すと、期待する結果になりました
ただし、毎回ファイルから読み込み直すので時間がどうなるのかが心配です
一応解決したという状態ですが、もうちょっとよい方法がないか研究中です

dobonさんのページも見て
Graphics.​Draw​Image を使う方法もやってみようと思いますが
これも、時間がかかるやり方なのではと考えています
投稿者 allgreen  (社会人) 投稿日時 2020/5/1 16:39:02
理解できない
その4
   画像.SelectActiveFrame(fd, 0)
  picturebox1.image=画像
   System.Windows.Forms.Application.DoEvents()
  画像.SelectActiveFrame(fd, 1)
  picturebox2.image=画像
   System.Windows.Forms.Application.DoEvents()
  画像.SelectActiveFrame(fd, 2)
  picturebox3.image=画像
   System.Windows.Forms.Application.DoEvents()
    picturebox4.image=picturebox1.image
    picturebox5.image=picturebox2.image
    picturebox6.image=picturebox3.image
        結果は、picturebox1は、ページ0
                    picturebox2は、ページ1
                    picturebox3は、ページ2
                    picturebox4は、ページ2
                    picturebox5は、ページ2
                    picturebox6は、ページ2
変数の代入の概念がくずれる?
投稿者 葉月  (社会人) 投稿日時 2020/5/1 22:05:01
Public Class Form1

    Private STR_FILE_PATH = "画像パス"
    Private listImgs As List(Of Image)

    Public Sub New()
        InitializeComponent()
        listImgs = New List(Of Image)()
    End Sub

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
        ShowImage(STR_FILE_PATH)
    End Sub

    Private Sub ShowImage(ByVal fileName As String)
        Dim canvas As Bitmap = New Bitmap(PictureBox1.Width, PictureBox1.Height)
        Dim g As Graphics = Graphics.FromImage(canvas)
        Dim img As Image = Image.FromFile(STR_FILE_PATH)
        Dim fd As System.Drawing.Imaging.FrameDimension = New System.Drawing.Imaging.FrameDimension(img.FrameDimensionsList(0))
        Dim frameCount As Integer = img.GetFrameCount(fd)
        Dim y As Integer = 0

        For i As Integer = 0 To frameCount - 1
            img.SelectActiveFrame(fd, i)
            Dim imgClone As Image = CType(img.Clone(), Image)
            listImgs.Add(imgClone)
            'imgClone.Dispose()
        Next

        img.Dispose()
        g.Dispose()
        PictureBox1.Image = canvas
    End Sub

    Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
        Dim canvas As Bitmap = New Bitmap(PictureBox1.Width, PictureBox1.Height)
        Dim g As Graphics = Graphics.FromImage(canvas)
        Dim x As Integer = 0, y As Integer = 0

        For Each img As Image In listImgs
            g.DrawImage(img, x, y, img.Width, img.Height)

            If x < PictureBox1.Width Then
                x += img.Width
            Else
                y += img.Height
                x = 0
            End If
        Next

        g.Dispose()
        PictureBox1.Image = canvas
    End Sub

End Class
投稿者 葉月  (社会人) 投稿日時 2020/5/1 22:49:49
サンプルコードはピクチャーボックスとボタンを配置しています。

解決おめでとうございます。一応サンプル作ったのでのせておきます。
'imgClone.Dispose()
その1からその4の疑問ですが、上記箇所のコメントを外すと例外になります。
Listに格納しているから正常に動くように見えますが、Listに格納した後も
imgCloneの変更が適用されているのがわかります。

>Imageをコピーするには?
サンプルの Dim imgClone As Image = CType(img.Clone(), Image)の部分で
コピーされています。
そのままコピペで動くコードなので試してください。

>ただし、毎回ファイルから
それよりPictureBoxを画像ファイルの数だけ複数用意するとメモリーを多く
使います。
(ピクチャーボックスが結構重いコントロールのため)
1個のピクチャーボックスに複数の画像を表示するのがお勧めです。
例として、いい加減ですがサムネイル風に表示しているのが以下の部分です。

If x < PictureBox1.Width Then
x += img.Width
Else
y += img.Height
x = 0
End If
投稿者 (削除されました)  () 投稿日時 2020/5/2 11:01:37
(削除されました)
投稿者 葉月  (社会人) 投稿日時 2020/5/2 11:04:07
サンプルコードのShowImageメソッドに余計な部分がありました。
画像フレームの取得と表示を、一つのメソッドで行っていたなごりです。
下記のコードに差し替え試してください。

Private Sub ShowImage(ByVal fileName As String)
        Dim img As Image = Image.FromFile(STR_FILE_PATH)
        Dim fd As System.Drawing.Imaging.FrameDimension = New System.Drawing.Imaging.FrameDimension(img.FrameDimensionsList(0))
        Dim count As Integer = img.GetFrameCount(fd)

        For i As Integer = 0 To count - 1
            img.SelectActiveFrame(fd, i)
            Dim imgClone As Image = CType(img.Clone(), Image)
            listImgs.Add(imgClone)
            'imgClone.Dispose()
        Next

        img.Dispose()
End Sub
投稿者 allgreen  (社会人) 投稿日時 2020/5/2 12:57:31
.clone というものを、やってみました
その5
   画像.SelectActiveFrame(fd, 0)
  picturebox1.image=画像.clone
   画像.SelectActiveFrame(fd, 1)
  picturebox2.image=画像.clone
   画像.SelectActiveFrame(fd, 2)
  picturebox2.image=画像.clone

この方法で、こちらの希望する結果になりました
pictureboxに対してうまくいってるので
image型変数に対してもうまくいくだろうと思います

ありがとうございました

投稿者 allgreen  (社会人) 投稿日時 2020/5/2 13:02:28
.clone をつけたら
image型変数のときも、希望通りの動作になりました

完全解決です

ありがとうございました