WmpBitmapEncoderを使ってwdp形式の画像を作成
作成時の画質の設定を変化させたときのファイルサイズと結果画像を比べてみた
この中で画質を指定するのに関係ありそうなのが以下の3つ(3つとは言っていない)
UseCodecOptions
QualityLevel
ImageQualityLevel
Lossless
UseCodecOptions
既定値はfalse、Trueにすると以下の3つが有効になる
QualityLevel、OverlapLevel、SubsamplingLevel
QualityLevel
既定値は10、1から255で指定,byte型、1で無損失
設定を有効にするにはUseCodecOptionsをTrueにしておく必要がある
1の無損失でもLosslessとはサイズが違う、少し小さくなった
ImageQualityLevel
既定値は0.9f、0から1で指定、float型、1で無損失
LosslessがTrueのときは無視される
UseCodecOptionsがTrueのときもQualityLevelが優先された
無損失だとLossless=trueの結果と同じになった
Lossless
既定値はfalse、Trueで無損失、可逆と不可逆の切り替え
UseCodecOptionsがTrueのときはQualityLevelが優先された
無損失だとImageQualityLevel=1.0fの結果と同じになった
優先順位、今回試した限りではこうだった
QualityLevel > Lossless > ImageQualityLevel
ただしQualityLevelはUseCodecOptionsをtrueにしておく必要がある
規定値のfalseだとQualityLevelは無効になる
使う画像は
いつもの
WmpBitmapEncoderの既定値
UseCodecOptionsはfalseなのでQualityLevelは無視されて
Losslessもfalseなので
ImageQualityLevelの0.9が適用される
これで作成すると
QualityLevel
1から255までを32ずつ変化
63でかなり劣化して、95以上は破綻している
255ではなぜか絵が出ているけどなんかへんだし
ファイルサイズも元画像より増えている
1から255まで指定できるけど実質1から100くらいかなあ
ImageQualityLevel
1.0から0.0まで0.1ずつ変化させた
QualityLevelよりもこちらのほうが使いやすそう
WmpBitmapEncoderの既定値でこちらを使っているのも納得できるし
既定値の0.9も画質をファイルサイズのバランスがいいと思う
元画像 144KB
Lossless=true 84KB
ImageQL 84KB
QualityLevel 78KB
QualityLevelが一番縮んで、ImageQualityLevelとLosslessTrueは同じサイズで
他の画像形式と比べてみると
TiffEncoder 128KB(圧縮設定はDefault)
PngEncoder 106KB
WmpBitmapEncoderの
ロスレスはかなり優秀なんじゃないかな
まとめると
WmpBitmapEncoderの画質設定は
ImageQualityLevelを使えばいい
最低品質を指定しても画像が破綻することもなく
その他の設定
ImageDataDiscardLevelってのを変更したらモザイクになった、
これ面白いねえ
サイズもめちゃくちゃ縮んで1/144とかモビルスーツがプラモデルになるレベル
Level1から3までファイルサイズが違っているので何かしら違うみたい
Level0だとグレースケールになった
OverlapLevel
これもファイルサイズ以外違いがわからん
元画像 4KB
Lossless=true 64KB
ImageQL 64KB
QualityLevel 77KB
残念なことに大幅に膨らんだ
写真画像が得意なのかも?
でも透明に関係しそうなalphaがついているプロパティがいくつかあるから
その辺を指定すると縮む?…としても
png形式にしたほうが早いし
思いついた、
Wmpの
ロスレスは写真画像に文字や矢印なんかの図形を入れたものを保存する時には良さそう!
拡張子
CodecInfoを見ると拡張子は.wdpか.jxrってある、どちらにするか迷う
HD Photo - Wikipedia
https://ja.wikipedia.org/wiki/HD_Photo
jxrのほうが新しいみたい
using System.Windows;
using System.Windows.Media.Imaging;
using System.IO;
namespace _20180116_WmpBitmapEncoder
{
<summary>
</summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Title = this.ToString();
string filePath;
filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Bgr24.bmp";
BitmapSource source = GetBitmapSource10(filePath, true, 96, 96);
WmpImageQualityLevel(source);
WmpQualityLevel(source);
WmpOverlapLevel(source);
WmpSubsamplingLevel(source);
WmpImageDataDiscardLevel(source);
WmpLossless(source);
}
private void WmpImageQualityLevel(BitmapSource source)
{
string fileName = "";
for (int i = 0; i <= 10; ++i)
{
var encoder = new WmpBitmapEncoder();
encoder.ImageQualityLevel = (float)i / 10;
encoder.Frames.Add(BitmapFrame.Create(source));
fileName = nameof(WmpImageQualityLevel) + encoder.ImageQualityLevel.ToString("0.0") + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}
private void WmpQualityLevel(BitmapSource source)
{
string fileName = "";
for (int i = 255; i > -32; i -= 32)
{
var encoder = new WmpBitmapEncoder();
if (i < 1) { encoder.QualityLevel = 1; }
else { encoder.QualityLevel = (byte)i; }
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.UseCodecOptions = true;
fileName = nameof(WmpQualityLevel) + encoder.QualityLevel.ToString("000") + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}
private void WmpOverlapLevel(BitmapSource source)
{
string fileName = "";
for (byte i = 0; i <= 2; ++i)
{
var encoder = new WmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.OverlapLevel = i;
encoder.UseCodecOptions = true;
fileName = nameof(WmpOverlapLevel) + encoder.OverlapLevel.ToString() + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}
private void WmpSubsamplingLevel(BitmapSource source)
{
string fileName = "";
for (byte i = 0; i <= 3; ++i)
{
var encoder = new WmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.SubsamplingLevel = i;
encoder.UseCodecOptions = true;
fileName = nameof(WmpSubsamplingLevel) + encoder.SubsamplingLevel.ToString() + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}
private void WmpImageDataDiscardLevel(BitmapSource source)
{
string fileName = "";
for (byte i = 0; i <= 3; ++i)
{
var encoder = new WmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.ImageDataDiscardLevel = i;
fileName = nameof(WmpImageDataDiscardLevel) + encoder.ImageDataDiscardLevel.ToString() + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}
private void WmpLossless(BitmapSource source)
{
string fileName = "";
var encoder = new WmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Lossless = true;
fileName = nameof(WmpLossless) + "_" + encoder.Lossless.ToString() + ".wdp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
<summary>
</summary>
<param name="filePath"></param>
<param name="accuratePixelFormat">
</param>
<param name="dpiX"></param>
<param name="dpiY"></param>
<returns></returns>
private BitmapSource GetBitmapSource10(
string filePath, bool accuratePixelFormat, double dpiX = 0, double dpiY = 0)
{
BitmapSource source;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
BitmapFrame bitmapFrame;
if (accuratePixelFormat)
{
bitmapFrame = BitmapFrame.Create(
fs,
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default);
}
else
{
bitmapFrame = BitmapFrame.Create(
fs,
BitmapCreateOptions.None,
BitmapCacheOption.Default);
}
int w = bitmapFrame.PixelWidth;
int h = bitmapFrame.PixelHeight;
int stride = (w * bitmapFrame.Format.BitsPerPixel + 7) / 8;
byte[] pixels = new byte[h * stride];
bitmapFrame.CopyPixels(pixels, stride, 0);
if (dpiX == 0) { dpiX = bitmapFrame.DpiX; }
if (dpiY == 0) { dpiY = bitmapFrame.DpiY; }
source = BitmapSource.Create(
w, h, dpiX, dpiY,
bitmapFrame.Format,
bitmapFrame.Palette, pixels, stride);
};
return source;
}
}
}
実行すると
filePath = @"D:\ブログ用\テスト用画像\
NEC_8041_2017_05_09_午後わてん_96dpi_Bgr24.
bmp";
とかで指定した画像ファイルを元に32個のwdpファイルが作成される
2018/01/17 20:19修正
int stride = (w * pixelFormat.BitsPerPixel) / 8;
↑を↓に書き直した
int stride = (w * pixelFormat.BitsPerPixel + 7) / 8;
これで8bpp以下で縦横
ピクセルともに8の倍数じゃない画像でもエラーにならない
2018/01/21
byte pixels = new byte[w * stride];
↑を↓に書き直した
byte pixels = new byte[h * stride];
前回の記事