1.ポインタを用いる
C#でもunsafeの範囲ではC言語同様ポインタを使える.
コード内にunsafe修飾子をつける.
/unsafe+ オプションをつけてコンパイルする必要がある.
###2.System.IO.MemoryStreamを用いる
System.IO.MemoryStream を使って読み込むとファイルのロックが任意のタイミングで解除できる.
new BitmapImage(new Uri(f))を使うとファイルがロックされてしまい,その後上書き・削除などができなくなることがままある.//必ず発生するわけではないことが厄介
サンプルの解説
AccessPixels(...)各ピクセルにアクセスするメソッド
in string fは読み込みたい画像ファイル(jpg,png可)のパス
int offset=(bitmap.PixelWidth*y+x)*4;はポインタの位置を示している.y,xに任意の座標を指定すればいい.
SaveBitmapSourceToFile(...)//変更データをbitmap形式でファイルに上書き保存.
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
private static unsafe void AccessPixels(in string f){
System.IO.MemoryStream data = new System.IO.MemoryStream(File.ReadAllBytes(f));
WriteableBitmap wbmp = new WriteableBitmap(BitmapFrame.Create(data));
data.Close();//MemoryStreamを閉じてロックを解除
FormatConvertedBitmap bitmap = new FormatConvertedBitmap(wbmp, PixelFormats.Pbgra32, null, 0);//32bit で読む
byte[] originalPixels = new byte[bitmap.PixelWidth * bitmap.PixelHeight * 4];//αの値も読みたいのでbgrαの4種 4*8bit=32bit
int stride = (bitmap.PixelWidth * bitmap.Format.BitsPerPixel + 7) / 8;
bitmap.CopyPixels(originalPixels, stride, 0);
for (int y = 0; y < bitmap.PixelHeight; ++y)
for (int x = 0; x < bitmap.PixelWidth; ++x) {
int offset = (bitmap.PixelWidth * y + x) * 4;
if(originalPixels[0 + offset] >=128)
originalPixels[0 + offset]=255;//b
else originalPixels[0 + offset]=0;//b
if(originalPixels[1 + offset] >=128)
originalPixels[1 + offset]=255;//g
else originalPixels[1 + offset]=0;//g
}
BitmapSource originalBitmap = BitmapSource.Create(bitmap.PixelWidth,bitmap.PixelHeight, 96, 96, PixelFormats.Pbgra32, null, originalPixels, stride);//変更データ(配列)をBitmapSourceに変換.
SaveBitmapSourceToFile(originalBitmap,f+".bmp")//変更データをファイルに保存.
//SaveAsPng(originalBitmap,f+".png")//変更データをpngで保存.
}
private void SaveBitmapSourceToFile(BitmapSource bitmapSource, string filePath){
using (var fileStream = new FileStream(filePath, FileMode.Create)){
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(fileStream);
}
}
public void SaveAsPng(BitmapSource bitmapSource,string pngPath){
using (var fileStream = new FileStream(pngPath, FileMode.Create)){
var encoder = new PngBitmapEncoder();
encoder.Interlace = PngInterlaceOption.On;
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(fileStream);
}
}
参考サイト
//ファイルをロックしないパターン
http://neareal.net/index.php?Programming%2F.NetFramework%2FWPF%2FWriteableBitmap%2FLoadReleaseableBitmapImage
// BitmapSourceから配列にコピー
https://water2litter.net/gin/?p=990
https://imagingsolution.net/program/csharp/bitmap-data-memory-format/
//BitmapSourceクラスをbitmapとして保存する
http://ni4muraano.hatenablog.com/entry/2017/10/13/080000
//BitmapSourceクラスをpngとして保存する
http://ni4muraano.hatenablog.com/entry/2018/01/29/080000
可読性が落ちるがもっと速くしたい場合,for以下を変更する
for (int i = 0; i < originalPixels.Length;){
if(originalPixels[i] >=128)
originalPixels[i++]=255;//b
else originalPixels[i++]=0;//b
if(originalPixels[i] >=128)
originalPixels[i++]=255;//g
else originalPixels[i++]=0;//g
originalPixels[i++]=originalPixels[i] >=128 ?255:0;//r
originalPixels[i++]=255;//α
}