画像を白黒2値にして1bitのビットマップ形式のファイルに保存
PixelFormatはBlackWhite
ついでPixelFormatがIndexed1(2色パレット)の1bitビットマップファイルも作成
いつものこの画像を
ヤフーブログはbmp画像の投稿には対応していないから
上の画像はpngに変換したもの
ファイルのプロパティ
ファイルサイズも6KBと小さい
256*192*1/8=6144bit≒6KB
白黒以外の1bit
PixelFormatはIndexed1
これは2色までのパレットを指定できるので
赤とオレンジのパレットで作成
デザイン画面
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
using System.Collections.Generic;
namespace _20180117_白黒2値画像作成
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Title = this.ToString();
string filePath;
filePath = @"D:\ブログ用\テスト用画像\NEC_8041_2017_05_09_午後わてん_96dpi_Rgb24.bmp";
//PixelFormatを8bitグレースケールに変換してBitmapSourceを取得
BitmapSource source = GetBitmapSourceWithCangePixelFormat2(filePath, PixelFormats.Gray8, 96, 96);
//画像作成しきい値を指定できる
BlackWhite2(source, 128);
}
/// <summary>
/// PixelFormatがGray8(8bitグレースケール)のBitmapSourceを白黒2値に変換して
/// 1bitビットマップ画像ファイルに保存
/// </summary>
/// <param name="source">PixelFormatがGray8のBitmapSource</param>
/// <param name="threshold">しきい値</param>
private void BlackWhite2(BitmapSource source, byte threshold)
{
int w = source.PixelWidth;
int h = source.PixelHeight;
int stride = w;//1ピクセル行のbyte数を指定、Gray8は1ピクセル8bitなのでw * 8 / 8 = w
byte[] pixels = new byte[h * stride];
source.CopyPixels(pixels, stride, 0);
//しきい値で白黒つける
for (int i = 0; i < pixels.Length; ++i)
{
if (pixels[i] < threshold)
{
pixels[i] = 0;
}
else
{
pixels[i] = 255;
}
}
//BitmapSource作成、ここでPixelFormatをBlackWhiteにすると画像が崩れるのでそのままで作成
BitmapSource newBitmap = BitmapSource.Create(w, h, source.DpiX, source.DpiY, source.Format, null, pixels, stride);
//PixelFormatをBlackWhiteに変換
FormatConvertedBitmap convertedBitmap = new FormatConvertedBitmap(newBitmap, PixelFormats.BlackWhite, null, 0);
MyImage.Source = convertedBitmap;//表示
//ファイルに保存
//1ビットのbmpファイル作成
string fileName = nameof(BlackWhite2) + ".bmp";
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
var encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(convertedBitmap));
encoder.Save(fs);
}
}
/// <summary>
/// ファイルパスとPixelFormatを指定してBitmapSourceを取得、dpiの変更は任意
/// </summary>
/// <param name="filePath">画像ファイルのフルパス</param>
/// <param name="pixelFormat">PixelFormatsの中からどれかを指定</param>
/// <param name="dpiX">無指定なら画像ファイルで指定されているdpiになる</param>
/// <param name="dpiY">無指定なら画像ファイルで指定されているdpiになる</param>
/// <returns></returns>
private BitmapSource GetBitmapSourceWithCangePixelFormat2(
string filePath, PixelFormat pixelFormat, double dpiX = 0, double dpiY = 0)
{
BitmapSource source;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
var bf = BitmapFrame.Create(fs);
var convertedBitmap = new FormatConvertedBitmap(bf, pixelFormat, null, 0);
int w = convertedBitmap.PixelWidth;
int h = convertedBitmap.PixelHeight;
int stride = (w * pixelFormat.BitsPerPixel + 7) / 8;
byte[] pixels = new byte[w * stride];
convertedBitmap.CopyPixels(pixels, stride, 0);
//dpi指定がなければ元の画像と同じdpiにする
if (dpiX == 0) { dpiX = bf.DpiX; }
if (dpiY == 0) { dpiY = bf.DpiY; }
//dpiを指定してBitmapSource作成
source = BitmapSource.Create(
w, h, dpiX, dpiY,
convertedBitmap.Format,
convertedBitmap.Palette, pixels, stride);
};
return source;
}
}
}
- 画像ファイルからBitmapSource作成
- PixelFormatをFormatConvertedBitmapでGray8に変換作成
- CopyPixelで色情報コピー
- 指定したしきい値で白と黒に分ける
- PixelFormatGray8のままBitmapSource作成
- FormatConvertedBitmapでPixelFormatをGray8からBlackWhiteに変換して作成
- BitmapFrameを作成してファイルに保存
BitmapSourceを5回も作っている?CopyPixelもカウントしたらさらに+1
ムダが多い気がするけど、この方法が分かりやすかった
4番でラクをするために2番で8bitグレースケールにしている、グレースケール変換はWPFにおまかせしているので、ここでもラクをしている
GetBitmapSourceWithCangePixelFormat2
これもこの前の記事のコピペ
楽ちん
2番の
PixelFormatをFormatConvertedBitmapでGray8に変換作成
この状態で読み込まれて
ここから白黒2値にするんだけど
これをGray8じゃなくて直接BlackWhiteに変換作成すると
ディザリングされた白黒2値になってしまう
今回はディザリングなしの白黒2値が目的だからねえ
5番の
PixelFormatGray8のままBitmapSource作成の
BitmapSource newBitmap = BitmapSource.Create(w, h, source.DpiX, source.DpiY, source.Format, null, pixels, stride);
ここをBlackWhiteで作成すると
BitmapSource newBitmap = BitmapSource.Create(w, h, source.DpiX, source.DpiY, PixelFormats.BlackWhite, null, pixels, stride);
崩壊する
strideとPixelFormatのbppが噛み合っていないからかなあ
白黒2値じゃなくて他の色2色にするときは
//PixelFormatをBlackWhiteに変換
FormatConvertedBitmap convertedBitmap = new FormatConvertedBitmap(newBitmap, PixelFormats.BlackWhite, null, 0);
↑をコメントアウトして
↓を書き加える
//パレット指定してIndexed1
List<Color> list = new List<Color> { Colors.Red, Colors.Orange };
BitmapPalette palette = new BitmapPalette(list);
FormatConvertedBitmap convertedBitmap = new FormatConvertedBitmap(newBitmap, PixelFormats.Indexed1, palette, 0);
List<Color> list = new List<Color> { Colors.Red, Colors.Beige };
BitmapPalette palette = new BitmapPalette(list);
ここでパレットに赤と(オレンジは目が痛かったので)ベージュにしている
結果は
List<Color> list = new List<Color> { Colors.Red, Colors.Beige };
これを
List<Color> list = new List<Color> { Colors.Beige, Colors.Red };
にしたら色が入れ替わるかな?
変化なし…
どうやら指定した2色を白黒どちらに近いか判定して
元の白黒の配置に近くなるようにしているみたい
しきい値
これだとプランターがあるのがわかる
70
これは白すぎた
プランターは消え去ったけど、いちごが見やすくなった
関連記事
WPF、画像ファイルを開く方法まとめ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15325331.html