この記事は
カラー画像を1bpp(1bit)白黒画像に変換して保存するアプリ作ってみた、しきい値は手動設定 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15335812.html
↑の記事のアプリのコード
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Windows.Controls.Primitives;
namespace _20180121_白黒2値に変換して保存
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
BitmapSource OriginBitmap;
int[] OriginHistogram;
string ImageFileFullPath;
public MainWindow()
{
InitializeComponent();
this.Title = this.ToString();
this.AllowDrop = true;
this.Drop += MainWindow_Drop;
AddComboBoxItem();//コンボボックス初期化
ButtonSaveImage.Click += ButtonSaveImage_Click;
ButtonAverageBrightness.Click += ButtonAverageBrightness_Click;
RadioOrigin.Click += RadioOrigin_Click;
RadioBlackWhite.Click += RadioBlackWhite_Click;
ScrollNumeric.ValueChanged += ScrollNumeric_ValueChanged;
ScrollNumeric.MouseWheel += ScrollNumeric_MouseWheel;
TextNumeric.GotFocus += TextNumeric_GotFocus;
TextNumeric.MouseWheel += TextNumeric_MouseWheel;
}
//テキストボックの上でマウスホイール回でスクロールバーの値を上下
private void TextNumeric_MouseWheel(object sender, MouseWheelEventArgs e)
{
TextBox textBox = (TextBox)sender;
Binding binding = BindingOperations.GetBinding(textBox, TextBox.TextProperty);
ScrollBar scrollBar = (ScrollBar)this.FindName(binding.ElementName);
if (e.Delta > 0) { scrollBar.Value++; }
else { scrollBar.Value--; }
}
//スクロールバーの上でマウスホイール回転で値を上下
private void ScrollNumeric_MouseWheel(object sender, MouseWheelEventArgs e)
{
ScrollBar scrollBar = (ScrollBar)sender;
if (e.Delta > 0) { scrollBar.Value++; }
else { scrollBar.Value--; }
}
//テキストボックスクリック時にテキスト全選択
private void TextNumeric_GotFocus(object sender, RoutedEventArgs e)
{
TextBox box = (TextBox)sender;
this.Dispatcher.InvokeAsync(() => { Task.Delay(10); box.SelectAll(); });
}
//画像の平均輝度をしきい値に設定
private void ButtonAverageBrightness_Click(object sender, RoutedEventArgs e)
{
if (OriginBitmap == null) { return; }
ScrollNumeric.Value = GetAvegareBrightness(OriginBitmap);
}
//スクロールバーの値変更したら画像の再2値化
private void ScrollNumeric_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (OriginBitmap == null) { return; }
ChangeBlackWhite();
}
//白黒2値ラジオボタンチェックありで2値化
private void RadioBlackWhite_Click(object sender, RoutedEventArgs e)
{
if (OriginBitmap == null) { return; }
if (RadioBlackWhite.IsChecked == true)
{
ScrollNumeric.IsEnabled = true;
TextNumeric.IsEnabled = true;
ChangeBlackWhite();
ButtonSaveImage.IsEnabled = true;
}
}
//元のグレースケール画像に戻す
private void RadioOrigin_Click(object sender, RoutedEventArgs e)
{
if (OriginBitmap == null) { return; }
if (RadioOrigin.IsChecked == true)
{
ScrollNumeric.IsEnabled = false;
TextNumeric.IsEnabled = false;
ButtonSaveImage.IsEnabled = false;
MyImage.Source = OriginBitmap;
}
}
//画像保存ボタン
private void ButtonSaveImage_Click(object sender, RoutedEventArgs e)
{
if (OriginBitmap == null) { return; }
SaveImage();
}
//画像ファイルがドロップされた時
private void MainWindow_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop) == false) { return; }
string[] filePath = (string[])e.Data.GetData(DataFormats.FileDrop);
//OriginBitmap = GetBitmapSourceWithCangePixelFormat2(filePath[0], PixelFormats.Pbgra32, 96, 96);
//OriginBitmap = GetBitmapSourceWithCangePixelFormat2(filePath[0], PixelFormats.Bgr24, 96, 96);
//8bitグレースケールに変換して読み込む
OriginBitmap = GetBitmapSourceWithCangePixelFormat2(filePath[0], PixelFormats.Gray8, 96, 96);
//OriginBitmap = GetBitmapSourceWithCangePixelFormat2(filePath[0], PixelFormats.Rgb24, 96, 96);
//OriginBitmap = GetBitmapSourceWithCangePixelFormat2(filePath[0], PixelFormats.Gray4, 96, 96);
if (OriginBitmap == null)
{
MessageBox.Show("not Image");
}
else
{
MyImage.Source = OriginBitmap;
OriginHistogram = GetHistogram(OriginBitmap);//ヒストグラム取得
ImageFileFullPath = System.IO.Path.GetFullPath(filePath[0]);//ファイルのフルパス取得
RadioBlackWhite.IsEnabled = true;
RadioOrigin.IsChecked = true;
ButtonSaveImage.IsEnabled = false;
ScrollNumeric.IsEnabled = false;
TextNumeric.IsEnabled = false;
}
}
//コンボボックス初期化
private void AddComboBoxItem()
{
ComboboxTiffCompress.Items.Add(TiffCompressOption.Ccitt3);
ComboboxTiffCompress.Items.Add(TiffCompressOption.Ccitt4);
ComboboxTiffCompress.Items.Add(TiffCompressOption.Default);
ComboboxTiffCompress.Items.Add(TiffCompressOption.Lzw);
ComboboxTiffCompress.Items.Add(TiffCompressOption.None);
ComboboxTiffCompress.Items.Add(TiffCompressOption.Rle);
ComboboxTiffCompress.Items.Add(TiffCompressOption.Zip);
ComboboxTiffCompress.SelectedItem = TiffCompressOption.Default;
}
/// <summary>
/// PixelFormatがGray8(8bitグレースケール)のBitmapSourceを白黒2値に変換して
/// </summary>
/// <param name="source">PixelFormatがGray8のBitmapSource</param>
/// <param name="threshold">しきい値</param>
private void BlackWhite2(BitmapSource source, int 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;//表示
}
//画像を2値化
private void ChangeBlackWhite()
{
BlackWhite2(OriginBitmap, (int)ScrollNumeric.Value);
}
//画像保存
private void SaveImage()
{
var saveFileDialog = new Microsoft.Win32.SaveFileDialog();
saveFileDialog.Filter = "*.png|*.png|*.jpg|*.jpg;*.jpeg|*.bmp|*.bmp|*.gif|*.gif|*.tiff|*.tiff|*.wdp|*.wdp;*jxr";
saveFileDialog.AddExtension = true;//ファイル名に拡張子追加
//初期フォルダ指定、開いている画像と同じフォルダ
saveFileDialog.InitialDirectory = System.IO.Path.GetDirectoryName(ImageFileFullPath);
saveFileDialog.FileName = GetSaveFileName();
if (saveFileDialog.ShowDialog() == true)
{
BitmapEncoder encoder = null;
switch (saveFileDialog.FilterIndex)
{
case 1:
encoder = new PngBitmapEncoder();
break;
case 2:
encoder = new JpegBitmapEncoder();
break;
case 3:
encoder = new BmpBitmapEncoder();
break;
case 4:
encoder = new GifBitmapEncoder();
break;
case 5:
//tiffは圧縮方式をコンボボックスから取得
var tiff = new TiffBitmapEncoder();
tiff.Compression = (TiffCompressOption)ComboboxTiffCompress.SelectedItem;
encoder = tiff;
break;
case 6:
//wmpはロスレス指定、じゃないと1bppで保存時に画像が崩れるしファイルサイズも大きくなる
var wmp = new WmpBitmapEncoder();
//wmp.ImageQualityLevel = 1.0f;
encoder = wmp;
break;
default:
break;
}
encoder.Frames.Add(BitmapFrame.Create(GetSaveImage()));
using (var fs = new FileStream(saveFileDialog.FileName, FileMode.Create, FileAccess.Write))
{
encoder.Save(fs);
}
}
}
//保存時の初期ファイル名取得
private string GetSaveFileName()
{
string fileName = "";
fileName = System.IO.Path.GetFileNameWithoutExtension(ImageFileFullPath);
if (Radio1bpp.IsChecked == true) { fileName += "_1bpp白黒2値"; }
else if (Radio8bpp.IsChecked == true) { fileName += "_8bpp白黒2値"; }
else { fileName += "_32bpp白黒2値"; }
return fileName;
}
//保存画像を取得、表示している2値化画像を指定のbppに対応したPixelFormatに変換する
private BitmapSource GetSaveImage()
{
PixelFormat pixelFormat;
if (Radio1bpp.IsChecked == true) { pixelFormat = PixelFormats.BlackWhite; }
else if (Radio8bpp.IsChecked == true) { pixelFormat = PixelFormats.Gray8; }
else { pixelFormat = PixelFormats.Bgr32; }
return new FormatConvertedBitmap((BitmapSource)MyImage.Source, pixelFormat, null, 0);
}
private BitmapSource ChangePixelFormat(PixelFormat pixelFormat)
{
return new FormatConvertedBitmap((BitmapSource)MyImage.Source, pixelFormat, null, 0);
}
//画像全体の平均輝度取得
private double GetAvegareBrightness(BitmapSource source)
{
long pixelsCount = 0;
long totalBrightness = 0;
for (int i = 0; i < OriginHistogram.Length; ++i)
{
pixelsCount += OriginHistogram[i];
totalBrightness += OriginHistogram[i] * i;
}
return totalBrightness / pixelsCount;
}
//全体の輝度値の平均をしきい値にする
private int GetThresholdAverage(BitmapSource source)
{
int Pixels画素数 = 0;
long Sum累計輝度 = 0;
for (int i = 0; i < 256; ++i)
{
Pixels画素数 += OriginHistogram[i];
Sum累計輝度 += OriginHistogram[i] * i;
}
int Ave平均輝度 = 0;//平均
Ave平均輝度 = (int)(Sum累計輝度 / Pixels画素数);
return Ave平均輝度;
}
//ヒストグラム作成
private int[] GetHistogram(BitmapSource source)
{
int[] OriginHistogram = new int[256];
int w = source.PixelWidth;
int h = source.PixelHeight;
int stride = w;
byte[] pixels = new byte[h * w];
source.CopyPixels(pixels, stride, 0);
for (int i = 0; i < pixels.Length; ++i)
{
OriginHistogram[pixels[i]]++;
}
return OriginHistogram;
}
/// <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 = null;
try
{
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[h * 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);
};
}
catch (Exception)
{
}
return source;
}
}
}