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

WPF、カラー画像を白黒2値や2色の1bitビットマップ画像ファイルにしてみた

$
0
0

画像を白黒2値にして1bitのビットマップ形式のファイルに保存
PixelFormatはBlackWhite
ついでPixelFormatがIndexed1(2色パレット)の1bitビットマップファイルも作成


いつものこの画像を

イメージ 1
白黒2値に変換、PixelFormatはBlackWhite
ヤフーブログはbmp画像の投稿には対応していないから
上の画像はpngに変換したもの

ファイルのプロパティ
イメージ 2
ビットの深さ1(1bit)になっている
ファイルサイズも6KBと小さい
256*192*1/8=6144bit≒6KB


白黒以外の1bit
イメージ 3
PixelFormatはIndexed1
これは2色までのパレットを指定できるので
赤とオレンジのパレットで作成



デザイン画面
イメージ 4
確認用のImageをおいただけ


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;
}
}
}


  1. 画像ファイルからBitmapSource作成
  2. PixelFormatをFormatConvertedBitmapでGray8に変換作成
  3. CopyPixelで色情報コピー
  4. 指定したしきい値で白と黒に分ける
  5. PixelFormatGray8のままBitmapSource作成
  6. FormatConvertedBitmapでPixelFormatをGray8からBlackWhiteに変換して作成
  7. BitmapFrameを作成してファイルに保存

BitmapSourceを5回も作っている?CopyPixelもカウントしたらさらに+1
ムダが多い気がするけど、この方法が分かりやすかった
4番でラクをするために2番で8bitグレースケールにしている、グレースケール変換はWPFにおまかせしているので、ここでもラクをしている
GetBitmapSourceWithCangePixelFormat2
これもこの前の記事のコピペ
楽ちん


2番の
PixelFormatをFormatConvertedBitmapでGray8に変換作成
イメージ 14
この状態で読み込まれて
ここから白黒2値にするんだけど

これをGray8じゃなくて直接BlackWhiteに変換作成すると
イメージ 13
ディザリングされた白黒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);
イメージ 12
崩壊する
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);


イメージ 5

List<Color> list = new List<Color> { Colors.Red, Colors.Beige };
BitmapPalette palette = new BitmapPalette(list);

ここでパレットに赤と(オレンジは目が痛かったので)ベージュにしている
結果は
イメージ 6
List<Color> list = new List<Color> { Colors.Red, Colors.Beige };
これを
List<Color> list = new List<Color> { Colors.BeigeColors.Red };
にしたら色が入れ替わるかな?
イメージ 7
変化なし…
どうやら指定した2色を白黒どちらに近いか判定して
元の白黒の配置に近くなるようにしているみたい





しきい値
イメージ 9
100
これだとプランターがあるのがわかる

イメージ 10
70
これは白すぎた

イメージ 11
140
プランターは消え去ったけど、いちごが見やすくなった

イメージ 8
List<Color> list = new List<Color> { Colors.DarkBlue, Colors.DarkMagenta };




関連記事
WPF、画像ファイルを開く方法まとめ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15325331.html










Viewing all articles
Browse latest Browse all 420

Trending Articles