テストアプリダウンロード先
時間のかかりがちな画像処理のときに
できた!
最初に作る時には前回の
どうやら別スレッドでBitmapSourceを直接変更するような処理はできないみたい?
ってことでいくつか試してみた
別スレッドで実行するメソッドへ渡す引数を
A: int
B: string
C: BitmapSource.Width
D: BitmapSource.Widthを入れたint
で試したら
CのBitmapSourceだけがエラーになる
A: int i = await Task.Run
*1; //OK
C: int i = await Task.Run(() => BitmapSource.PixelWidth * 2); //エラー
D: int w = BitmapSource.PixelWidth;
int i = await Task.Run(() => w * 2); //OK
Cみたいに直接BitmapSourceのWidthを渡して処理しようとするとエラーになるけど
Dのようにint型の変数に入れたのを引数にすればエラーにならない
デザイン画面
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
namespace _20180507_BitmapSourceは別スレッドで処理できない
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyButtonInteger.Click += MyButtonInteger_Click;
MyButtonString.Click += MyButtonString_Click;
MyButtonBitmap.Click += MyButtonBitmap_Click;
MyButtonBitmap2.Click += MyButtonBitmap2_Click;
}
private async void MyButtonInteger_Click(object sender, RoutedEventArgs e)
{
int i = await Task.Run(() => 100 * 2);
MyTextBlock.Text = i.ToString();
}
private async void MyButtonString_Click(object sender, RoutedEventArgs e)
{
int i = await Task.Run(() => ("test文字数").Count());
MyTextBlock.Text = i.ToString();
}
private async void MyButtonBitmap_Click(object sender, RoutedEventArgs e)
{
var source = new BitmapImage(new Uri(@"D:\ブログ用\チェック用2\NEC_2820_2018_05_06_午後わてん.jpg"));
int i = await Task.Run(() => source.PixelWidth * 2);
MyTextBlock.Text = i.ToString();
}
private async void MyButtonBitmap2_Click(object sender, RoutedEventArgs e)
{
var source = new BitmapImage(new Uri(@"D:\ブログ用\チェック用2\NEC_2820_2018_05_06_午後わてん.jpg"));
int w = source.PixelWidth;
int i = await Task.Run(() => w * 2);
MyTextBlock.Text = i.ToString();
}
}
}
これでBitmapSourceやBitmapSourceのプロパティを直接渡すとエラーになるけど
変数に入れて渡せばエラーにならず別スレッドで処理できそうなので
できたのが最初の画像処理アプリ
デザイン画面
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Threading;
namespace _20180507_別スレッドで画像処理
{
public partial class MainWindow : Window
{
BitmapSource OriginBitmap;
CancellationTokenSource MyCancelSource;
public MainWindow()
{
InitializeComponent();
OriginBitmap = GetBitmapSourceWithChangePixelFormat2(
@"D:\ブログ用\チェック用2\NEC_2773_2018_05_05_午後わてん_.jpg", PixelFormats.Pbgra32, 96, 96);
MyImage.Source = OriginBitmap;
MyButtonOrigin.Click += MyButtonOrigin_Click;
MyButton1.Click += MyButton1_Click;
MyButtonCancel.Click += MyButtonCancel_Click;
MyButtonCancel.IsEnabled = false;
}
private void MyButtonCancel_Click(object sender, RoutedEventArgs e)
{
MyCancelSource.Cancel();
}
private async void MyButton1_Click(object sender, RoutedEventArgs e)
{
MyButtonボタンの有効設定(true);
var wb = new WriteableBitmap(OriginBitmap);
int w = wb.PixelWidth;
int h = wb.PixelHeight;
int stride = wb.BackBufferStride;
byte[] pixels = new byte[h * stride];
wb.CopyPixels(pixels, stride, 0);
MyCancelSource = new CancellationTokenSource();
CancellationToken token = MyCancelSource.Token;
Progress<int> progress = new Progress<int>(MyProgressUpdate);
await Task.Run(() => ColorReverce(pixels, w, h, stride, token, progress));
wb.WritePixels(new Int32Rect(0, 0, w, h), pixels, stride, 0);
MyImage.Source = wb;
MyButtonボタンの有効設定(false);
}
private void MyButtonOrigin_Click(object sender, RoutedEventArgs e)
{
MyImage.Source = OriginBitmap;
}
private void MyProgressUpdate(int i)
{
MyProgressBar.Value = i;
}
private void MyButtonボタンの有効設定(bool is処理中)
{
if (is処理中 == true)
{
MyButtonCancel.IsEnabled = true;
MyButtonOrigin.IsEnabled = false;
MyButton1.IsEnabled = false;
}
else
{
MyButtonCancel.IsEnabled = false;
MyButtonOrigin.IsEnabled = true;
MyButton1.IsEnabled = true;
}
}
private bool ColorReverce(byte[] pixels, int w, int h, int stride,
CancellationToken token, IProgress<int> progress)
{
long p;
for (int y = 0; y < h; ++y)
{
if (token.IsCancellationRequested == true)
{
return false;
}
for (int x = 0; x < w; ++x)
{
p = y * stride + x * 4;
pixels[p] = (byte)(255 - pixels[p]);
pixels[p + 1] = (byte)(255 - pixels[p + 1]);
pixels[p + 2] = (byte)(255 - pixels[p + 2]);
}
progress.Report(y * 100 / (h - 1));
Thread.Sleep(10);
}
return true;
}
private BitmapSource GetBitmapSourceWithChangePixelFormat2(
string filePath, PixelFormat pixelFormat, double dpiX = 0, double dpiY = 0)
{
BitmapSource source = null;
try
{
using (System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.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);
if (dpiX == 0) { dpiX = bf.DpiX; }
if (dpiY == 0) { dpiY = bf.DpiY; }
source = BitmapSource.Create(
w, h, dpiX, dpiY,
convertedBitmap.Format,
convertedBitmap.Palette, pixels, stride);
};
}
catch (Exception) { }
return source;
}
}
}
今まではBitmapSourceを受け取って処理していたけど、別スレッドで処理する場合はそれだとエラーになるのでbyte型配列とint型だけ受け取るように変更(80行目)
100行目でウェイトをかけているのは、色の反転処理はあっという間に終わってキャンセルボタンを押せないから
36~41行目と47,48行目は以前では画像処理で行っていたところ
46行目で画像処理を別スレッドで行っている
それ以外は前回と同じ
やっとできたけど
BitmapSourceはButtonやTextBlockと同じUIってことなのかねえ
そもそもUIってのがイマイチ理解できていない
それに別スレッドで行うメソッドに引数を渡す時に
BitmapSource.Widthそのままだとエラーになるけど
int w = BitmapSource.Width
って変数に入れて渡せばエラーにならないってのも、そうなのかあって感じ
参照すること自体がUIスレッドにアクセスするってことだから別スレッドエラーで
int型は参照型じゃなくて値型だからint型変数に入れれば参照じゃないからOK
ってことかなあ
それでも今回ので画像処理中の進捗率表示と処理キャンセルっていう目的は達成できた
コード
アプリ
B: int i = await Task.Run(() => ("test文字数").Count(