午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPFとVB.NET、複数のBitmapSourceを1つにしてシリアライズしてファイルに保存

WPFVBで複数画像(BitmapSource)とシリアライズした文字列(String)や数値(Integerとか)を1つのZipファイルにする ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
http://blogs.yahoo.co.jp/gogowaten/14079902.html
↑新しい記事を書いたので
↓この記事はあんまり意味ないかも?
 
WindowsFormのBitmapはそのままでもシリアライズできたけど
WPFのBitmapSourceはできない!けど文字列に変化すればできるみたい
 
 

複数のBitmapSource(画像)をシリアライズして1つのファイルに保存

 
イメージ 1
実行ボタン押すと
シリアライズ→ファイルに保存→ファイル読み込み→デシリアライズ→画像表示
 
イメージ 2
こう
 
デザイン画面とXAML

f:id:gogowaten:20191025125514p:plain

VBコード

f:id:gogowaten:20191025125523p:plain

Imports System.IO
Imports System.Runtime.Serialization
Class MainWindow
    Private bmps As New List(Of BitmapSource)
    '    c# - Serialize a System.Windows.Media.ImageSource object - Stack Overflow
    'http://stackoverflow.com/questions/7262060/serialize-a-system-windows-media-imagesource-object?lq=1
    'これがいい!
    Private Sub testBitmap()
        'BitmapSourceからBitmapFrame作成
        'tiff画像に変換してMemoryStreamにSave
        'MemoryStreamをStringに変換
        'Stringをシリアライズしてファイルに保存

        '複数画像を1つのTIFF画像に変換
        Dim encoder As New TiffBitmapEncoder
        Dim frame As BitmapFrame
        For i As Integer = 0 To bmps.Count - 1
            frame = BitmapFrame.Create(bmps(i))
            encoder.Frames.Add(frame)
        Next

        'TIFF画像を文字列に変換してからシリアライズ
        Dim str As String 'Dim bh As Byte()
        Using ms As New MemoryStream()
            encoder.Save(ms)
            str = Convert.ToBase64String(ms.ToArray) '文字列に変換            'bh = ms.ToArray
        End Using

        'バイナリ形式でファイルに保存
        Dim fileName As String = "D:\ブログ用\WPF\BitmapSerializeString.bin"
        Using fs As New FileStream(fileName, FileMode.Create)
            Dim bf As New Formatters.Binary.BinaryFormatter
            bf.Serialize(fs, str)
        End Using


        'デシリアライズ
        'ファイルから読み込んだバイナリをデシリアライズして文字列に変換
        Dim deStr As String        'Dim debh As Byte()
        Using fs As New FileStream(fileName, FileMode.Open)
            Dim bf As New Formatters.Binary.BinaryFormatter
            deStr = bf.Deserialize(fs)
        End Using

        '文字列をTIFF画像に変換して表示
        Dim bytes() As Byte = Convert.FromBase64String(deStr)
        Using ms As New MemoryStream(bytes)
            Dim decoder As New TiffBitmapDecoder(ms, BitmapCreateOptions.None, BitmapCacheOption.OnLoad)

            '画像の表示
            img3.Source = decoder.Frames.Item(0)
            img4.Source = decoder.Frames.Item(1)
        End Using

    End Sub
    Private Sub MainWindow_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized
        Dim filepath As String = "D:\ブログ用\テスト用画像\hueRect030.png"
        Dim bmpi As New BitmapImage(New Uri(filepath))
        img1.Source = New BitmapImage(New Uri(filepath))
        bmps.Add(bmpi)

        filepath = "D:\ブログ用\テスト用画像\hueRect210.png"
        bmpi = New BitmapImage(New Uri(filepath))
        img2.Source = bmpi
        bmps.Add(bmpi)
    End Sub
    Private Sub bt1_Click(sender As Object, e As RoutedEventArgs) Handles bt1.Click
        Call testBitmap()
    End Sub
End Class
 
 
ファイルに保存までの流れ
それぞれの画像をTiff画像に変換
Tiff画像を文字列(Base64String)に変換
文字列をシリアライズしてバイナリ形式で保存
 
ファイルを読み込んで画像表示までの流れ
バイナリファイルをデシリアライズして文字列に変換
文字列をbyte配列に変換
byte配列をTiff画像に変換
それぞれの画像を表示
 
こんな感じ(よくわかっていない)
参照したのはここ

stackoverflow.com

ここではpng画像にしているけど
今回のは複数の画像を1つのファイルに入れることができるTIFF画像を使った
 
イメージ 5
MemoryStreamとかConvert.ToBase64String
この辺初めて使ったかも
 
 
 
画像だけならシリアライズしないでそのままTIFF画像で保存すればいいけど

f:id:gogowaten:20191025125539p:plain

こういう編集状態を保存しておいて、次にアプリを起動した時に編集の続きを再開できるようにしたかった
そのためにはシリアライズが必要みたいだったので試してみたら
WPFのBitmapSource(画像)はそのままではシリアライズできない!
ってことでググッてコピペしてなんとかなったのでメモしたのが今回の記事
 
 
シリアライズして保存されたファイルは元の画像ファイルよりサイズが大きくなる
pngに向いた画像の場合
3.39KB→9.39KB
 
jpeg写真画像の場合
34KB →275KB
 
3~10倍になってしまうけどTIFF可逆圧縮形式だし、それを文字列に変換もしているからこれくらいはね
TIFFは馴染みがなかったけど複数画像を別々のまま1つのファイルにできるのは便利な規格だなあ
 
jpeg写真画像に使ったのは
イメージ 7
こんな感じ