Quantcast
Channel: 午後わてんのブログ
Viewing all articles
Browse latest Browse all 420

WPF、PixelFormatを変更した画像をファイルに保存、FormatConvertedBitmap

$
0
0
PixelFormatを変更してファイルに保存

イメージ 1
PixelFormatを変更している様子

コンボボックスからPixelFormatを選ぶと変更された画像に切り替わる
Saveボタンを押すとファイルに保存する
保存形式はbmp、jpeg、gif、png、tiff、wdp
ファイル名は元のファイル名+PixelFormatの名前
PixelFormatは25種類
Bgr555
Bgr565
Bgr24
Bgr32
Bgr101010
Bgra32
Pbgra32
Cmyk32
BlackWhite
Gray2
Gray4
Gray8
Gray16
Gray32Float
Indexed1
Indexed2
Indexed4
Indexed8
Rgb24
Rgb48
Rgb128Float
Rgba64
Rgba128Float
Prgba64
Prgba128Float


XAMLデザイン画面
イメージ 2


C#のコード

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;

//PixelFormats クラス(System.Windows.Media)

namespace _20180110_PixelFormatいろいろ
{
public partial class MainWindow : Window
{
private BitmapSource OriginalBitmapSource;//元画像を入れておく
private string OriginalFileName;//元画像のファイル名

public MainWindow()
{
InitializeComponent();
string filePath = "";
//filePath = @"D:\ブログ用\テスト用画像\青空とトマトの花.jpg";
filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_72dpi.jpg";

SetSource(filePath);
OriginalFileName = System.IO.Path.GetFileNameWithoutExtension(filePath);
DisplayOriginal();

ButtonSave.Click += ButtonSave_Click;
ComboBoxPixelFormats.SelectionChanged += ComboBoxPixelFormats_SelectionChanged;
AddComboBoxItem();
}

//コンボボックスの初期化、項目追加
private void AddComboBoxItem()
{
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgr555);
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgr565);
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgr24);
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgr32);
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgr101010);
ComboBoxPixelFormats.Items.Add(PixelFormats.Bgra32);
ComboBoxPixelFormats.Items.Add(PixelFormats.Pbgra32);
ComboBoxPixelFormats.Items.Add(PixelFormats.Cmyk32);
ComboBoxPixelFormats.Items.Add(PixelFormats.BlackWhite);
//ComboBoxPixelFormats.Items.Add(PixelFormats.Default);
ComboBoxPixelFormats.Items.Add(PixelFormats.Gray2);
ComboBoxPixelFormats.Items.Add(PixelFormats.Gray4);
ComboBoxPixelFormats.Items.Add(PixelFormats.Gray8);
ComboBoxPixelFormats.Items.Add(PixelFormats.Gray16);
ComboBoxPixelFormats.Items.Add(PixelFormats.Gray32Float);
ComboBoxPixelFormats.Items.Add(PixelFormats.Indexed1);
ComboBoxPixelFormats.Items.Add(PixelFormats.Indexed2);
ComboBoxPixelFormats.Items.Add(PixelFormats.Indexed4);
ComboBoxPixelFormats.Items.Add(PixelFormats.Indexed8);
ComboBoxPixelFormats.Items.Add(PixelFormats.Rgb24);
ComboBoxPixelFormats.Items.Add(PixelFormats.Rgb48);
ComboBoxPixelFormats.Items.Add(PixelFormats.Rgb128Float);
ComboBoxPixelFormats.Items.Add(PixelFormats.Rgba64);
ComboBoxPixelFormats.Items.Add(PixelFormats.Rgba128Float);
ComboBoxPixelFormats.Items.Add(PixelFormats.Prgba64);
ComboBoxPixelFormats.Items.Add(PixelFormats.Prgba128Float);
}

//コンボボックスの項目変更時
private void ComboBoxPixelFormats_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
//var neko = comboBox.SelectedItem;

//PixelFormatの変換
PixelFormat pixelFormat = (PixelFormat)comboBox.SelectedItem;
var convertedBitmap = new FormatConvertedBitmap( OriginalBitmapSource, pixelFormat, OriginalBitmapSource.Palette, 0);
MyImage.Source = convertedBitmap;
TextBlockBpp.Text = "ビットの深さ(bpp) = " + pixelFormat.BitsPerPixel.ToString();
}

//保存ボタン押した時
private void ButtonSave_Click(object sender, RoutedEventArgs e)
{
SaveImageJpeg();//jpeg形式でファイルに保存
SaveImageBmp();
SaveImagePng();
SaveImageGif();
SaveImageTiff();
SaveImageWmp();
}


//画像ファイルをdpi96に変換したものを取得
private void SetSource(string filePath)
{
WriteableBitmap wb;
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
wb = new WriteableBitmap(BitmapFrame.Create(fs));
}
int w = wb.PixelWidth;
int h = wb.PixelHeight;
int stride = wb.BackBufferStride;
byte[] pixels = new byte[h * stride];
wb.CopyPixels(pixels, stride, 0);
var bs = BitmapSource.Create(w, h, 96, 96, wb.Format, wb.Palette, pixels, stride);
OriginalBitmapSource = bs;
TextBlockPixelFormat.Text = " = " + bs.Format.ToString();
}

private void DisplayOriginal()
{
MyImage.Source = OriginalBitmapSource;
}


//jpeg形式でファイルに保存
private void SaveImageJpeg()
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
//MessageBox.Show(encoder.QualityLevel.ToString());//75
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".jpg";

using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private void SaveImageBmp()
{
var encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".bmp";

using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private void SaveImagePng()
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".png";

using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private void SaveImageGif()
{
var encoder = new GifBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".gif";

using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private void SaveImageTiff()
{
var encoder = new TiffBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".tiff";
var neko = encoder.Compression;
//encoder.Compression = TiffCompressOption.Ccitt3;
using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private void SaveImageWmp()
{
var encoder = new WmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)(MyImage.Source)));
string fileName = OriginalFileName + "_" + GetPixelFormatName() + ".wdp";
var neko = encoder.CodecInfo.FileExtensions;
using (var fs = new FileStream(fileName, FileMode.Create))
{
encoder.Save(fs);
}
}

private string GetPixelFormatName()
{
BitmapSource bitmapSource = (BitmapSource)MyImage.Source;
return bitmapSource.Format.ToString();
}
}
}


ファイルパスから画像取得
イメージ 3
前回と同じようにdpiを96に変換している
作成したBitmapSourceはあとでPixelFormatを変更するので
どこからでも参照できるフィールド変数?に入れておく
イメージ 4
ここのOriginalBitmapSource


コンボボックスの初期化、項目を追加する
イメージ 5
ComboBoxのItemsにPixelFormatをそのまま入れている
一個一個入れるんじゃなくて一括でできればいいんだけど、方法がわからん
でも、文字と数値しか入れられないイメージだったけど入るんだねえ、便利


コンボボックスの項目変更時にPixelFormatを変更
イメージ 6
変更に使うのはFormatConvertedBitmapって言うクラス
これをNewで作る時
FormatConvertedBitmap(BitmapSource, PixelFormat, BitmapPalette, Double)
引数のBitmapSourceとBitmapPaletteは元画像のものを渡して、
PixelFormatはComboBoxのSelectedItem、
最後のdoubleは透明度に関係あるっぽい、0を渡しとけばいいみたい
これでできたものをImageコントロールのSourceに指定すると表示される


ファイルとして保存
ファイル名にPixelFormatの名前を付けたかったので
PixelFormatの名前はToStringで取得
イメージ 8

jpeg形式で保存の場合
イメージ 7

bmp形式の場合
イメージ 9
他の形式も同じように細かい設定は何もしないで保存している

PixelFormatの中には128bitとかもあるのに、これでできているのかなあと疑問に思うので保存されたファイルを見てみると

PixelFormatがPrgba128Floatをbmp形式で保存したファイルのプロパティ
イメージ 10
ビットの深さは64になっている
ずいぶん減っているけど64bitのbmpって初めてみた
普通だと24とか32


次はgif画像
イメージ 11
8bit
gifは最大で8bitみたい


jpeg
イメージ 12
24bit
これもjpeg形式の最大値だと思う


png
イメージ 13
おお、64bit、これも初めてみた


tiff
イメージ 14
これも64
tiff画像は馴染みがないから、よくわからん


最後にwdp
イメージ 15
128!

エクスプローラーから見る限りでは各画像形式の上限らしきbitで保存できているみたい
元の画像が24bitだしモニタも普通の液晶ディスプレイだから以上になってもきれいにはならないけどね



次は1bit
イメージ 16
白か黒の2色のPixelFormatはBlackWhite
単純な2値化じゃなくて誤差拡散法のディザリングみたい

結果
形式bit
bmp1
gif8
jpeg8
png1
tiff1
wdp1
gifとjpegが8bitになっている、jpegはなんとなく仕方がないかなあって気もするけどgifはどうなんだろう、違う気がする
その他は同じ1bitで保存されている、けど
wdpだけうまく保存できていなくて

イメージ 17
これはEdgeで開いたところ、うまく保存できていない
ファイルサイズもbmp形式で6KBなのにwdpは0.3KBと極端に小さくなっている
PixelFormat.BlackWhiteをWmpBitmapEncoderで
正しく保存するにはコーデックの設定を変更する必要があるのかも

wdpって今回初めて見たんだけど
HD Photo - Wikipedia
https://ja.wikipedia.org/wiki/HD_Photo
JPEG XR - Wikipedia
https://ja.wikipedia.org/wiki/JPEG_XR
マイクロソフトが作ったもので
今ではJEPG XRって言うみたい?
拡張子もwdpじゃなくてjxrのほうが良かったかなあ

この画像形式を開くことができるアプリは
Windowsのフォト
Windows フォトビューワー
インターネットブラウザのEdge
エクスプローラーで表示形式を大きなアイコンとかにする
などWindows標準のアプリなら見ることができた

WindowsFormアプリのPixtack紫陽花は見られなかったけど
WPFアプリのPixtack紫陽花2ndなら見ることができた!


保存されたファイルのサイズ一覧
イメージ 32
コーデックの設定は全く変更していないので
jpegとwdpは参考程度
jpegのQualityは75だった


保存された画像ファイルのbpp一覧
イメージ 24
wdpはこの中では一番新しい形式なだけあって128bitにも対応している
気になるのがCmyk32をjpegで保存したファイル、32bitになっている

イメージ 18
jpegで32bitなんてあるのかな

エクスプローラーで見ると
イメージ 20
普通に表示されている
これを

ヤフーブログもjpegファイルは普通に投稿できるので
このファイルを載っけてみると
イメージ 19
あれ、色がぜんぜん違う!

google Chromeで表示
イメージ 21
妙に鮮やかになっている

ペイント
イメージ 22
正常、ほとんどのアプリは普通に表示された
でも色が変わるアプリもあるから
Cmyk32をjpeg形式で保存したファイルは、なんかおかしいんだろうねえ

色相を180度移動で反対の色にしてみたら
イメージ 23
ヤフーブログで表示される色に似ているねえ



イメージ 25
元が24bitだからそれ以上のフォーマットは意味ないけど一覧
面白いのは減色処理みたいになる、24未満になるものかな
Indexed1から8までの4つはパレットを持った画像になるみたい

Indexed1は1ビットなので2色
イメージ 26
この2色は
イメージ 27
これで
元の画像から選定してパレットに登録して使っているのかな
この色の選び方がスゴイと思う

他の画像で
イメージ 35
この画像のPixelFormatをIndexed1にすると
イメージ 36
違う2色になる

Indexed2は2ビットなので4色
イメージ 28
これがたった4色で表現されている

イメージ 29
この4色
どうやって選んでいるのかなあ

別の画像でIndexed2
イメージ 37
色は違うけどこれも4色
そういえば昔のプリンターは4色が普通だったなあ


Indexed4は4ビットなので16色
イメージ 30

Indexed8は8ビットなので256色
イメージ 31


gifファイルのハッシュ値一覧
イメージ 33
gifはすべて8bitのファイルが出来上がったけど中身は違っているのか気になった
とくにIndexed8は8bitだからgifの最大色数256色と同じだからどうかなってみたら
アルファチャンネルを含まないRGB系と全く同じ
パレットはどんな扱いになっているんだろうねえ

bmp
イメージ 34
Rgb101010はファイルサイズもファイルのbppも64bitや128bitのファイルと同じなのに中身は違うみたい
ファイルヘッダのbppを記録する部分が違うだけだったりして


追記
さっきの色が変わった画像を記事を投稿してからダウンロードしてみた
イメージ 38
ビットの深さ24になっている
ヤフーブログに32bitのjpeg画像をアップロードすると
その時点で24bitに変換されるってことかなあ
32bitのjpeg画像はありえないってことかしらw




Viewing all articles
Browse latest Browse all 420

Trending Articles