3
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[C#]画像に対していろいろ行う(System.Drawing.Bitmap版)

Last updated at Posted at 2020-03-31

もくじ
https://qiita.com/tera1707/items/4fda73d86eded283ec4f

画像に対していろいろやるシリーズ

やりたいこと

WPFの画面表示や画像回りでいろいろやることがあるが、なんだかややこしくて、まとめようと思ってもまとめきれないまま時間が過ぎている。
いきなりまとめるのをちょっとあきらめて、やりたいこと、やったことのレシピ集みたいなのを作って経験値を貯めてから、見えてきたものを後でまとめようと思う。
→主に、System.Drawing.Bitmapを使って画像をファイルに保存したり、編集したときのことをまずは書く。(あとは、出てきたやつを都度追記していく。)

画像ファイルを開いて、好きな文字やら図形を書き込んで、別のファイルに保存する(その1)

  1. 元画像ファイル(.bmp等)をSystem.Drawing.Bitmapに格納
  • System.Drawing.Graphicsで格納した画像を編集
  • 出力先ファイルへのFileStreamを作成
  • System.Drawing.BitmapSaveメソッドで、**出力先ファイルへのFileStream**を指定し保存
  • →これで、出力先ファイルに保存される
サンプル1.cs
using (var bmp = new Bitmap(@"input.bmp"))
using (var fs = new FileStream(@"output1.bmp", FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
    using (var g = Graphics.FromImage(bmp))
    {
        g.DrawString("あいうえお", new Font("Arial", 16), System.Drawing.Brushes.Red, new PointF(10.0f, 10.0f));
        g.DrawRectangle(Pens.Red, new System.Drawing.Rectangle(100, 100, 100, 100));
    }

    // 保存方法① streamで保存 → "output1.bmp"に保存される
    fs.SetLength(0);
    bmp.Save(fs, System.Drawing.Imaging.ImageFormat.Bmp);
}

※fs(ファイルストリーム)がClose=Disposeされた時点(つまり、usingを抜けた時点)で、実際にbmpファイルに書き出される。

画像ファイルを開いて、好きな文字やら図形を書き込んで、別のファイルに保存する(その2)

  1. 元画像ファイル(.bmp等)をSystem.Drawing.Bitmapに格納
  • System.Drawing.Graphicsで格納した画像を編集
  • System.Drawing.BitmapSaveメソッドで、出力先ファイルのパスを指定し保存
  • →これで、出力先ファイルに保存される
サンプル2.cs
using (var bmp = new Bitmap(@"input.bmp"))
{
    using (var g = Graphics.FromImage(bmp))
    {
        g.DrawString("あいうえお", new Font("Arial", 16), System.Drawing.Brushes.Red, new PointF(10.0f, 10.0f));
        g.DrawRectangle(Pens.Red, new System.Drawing.Rectangle(100, 100, 100, 100));
    }

    // 保存方法② ファイルパス指定で保存 → "output2.bmp"に保存される
    bmp.Save(@"output2.bmp");
}

画像ファイルを開いて、好きな文字やら図形を書き込んで、画面に表示する

FileStreamを使うやり方。

サンプル3-1.cs
using (var bmp = new Bitmap(@"input.bmp"))
using (var fs = new FileStream(@"output1.bmp", FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
    using (var g = Graphics.FromImage(bmp))
    {
        g.DrawString("あいうえお", new Font("Arial", 16), System.Drawing.Brushes.Red, new PointF(10.0f, 10.0f));
        g.DrawRectangle(Pens.Red, new System.Drawing.Rectangle(100, 100, 100, 100));
    }

    // 画面に表示 ※MyImageは、xamlに配置された<Image Name="MyImage"/>
    var a = BitmapFrame.Create(fs, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
    MyImage.Source = a;
}

下記でも、同じことができる。(ファイルをかまさずに、メモリ(MemoryStream)で済ます)

3-2.cs
using (var bmp = new Bitmap(@"input.bmp"))
using (var ms = new MemoryStream())
{
    using (var g = Graphics.FromImage(bmp))
    {
        g.DrawString("あいうえお", new Font("Arial", 16), System.Drawing.Brushes.Red, new PointF(10.0f, 10.0f));
        g.DrawRectangle(Pens.Red, new System.Drawing.Rectangle(100, 100, 100, 100));
    }

    //// MemoryStreamに一旦保存
    ms.SetLength(0);
    bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);

    // 画面に表示
    var a = BitmapFrame.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
    MyImage.Source = a;
}

画面表示を取り込んで、ファイルに保存する

以前の記事参照。
BitmapSourceやその派生クラスから、System.Drawing.Bitmapに変換する

.cs
// BitmapSourceの派生クラス「RenderTargetBitmap」で、画像を取ってくる
var canvas = new RenderTargetBitmap((int)MyImage.ActualWidth, (int)MyImage.ActualHeight, 96, 96, PixelFormats.Pbgra32);
canvas.Render(MyImage); // canvasに画像を描画

// BmpBitmapEncoderに画像を入れる
using (var stream = new MemoryStream())
{
    // BmpBitmapEncoderに画像を書きこむ(使うBitmapEncoderの派生クラスによって、いろんなフォーマットのファイルを作れる)
    //BitmapEncoder encoder = new BmpBitmapEncoder();
    BitmapEncoder encoder = new JpegBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(canvas));// canvasからBitmapEncoder に書き込み
    encoder.Save(stream);

    // BmpBitmapEncoderからSystem.Drawing.Bitmapをつくる
    var bitmap = new System.Drawing.Bitmap(stream);
    bitmap.Save(@".\out.jpeg");
}

画面表示を取り込んで、画像に四角を書き込んで、それをもう一回画面に表示する

ややこしい。下記のようなクラスを経由していく流れ。

  • RenderTargetBitmap(画面から画像の取り込み)
  • BmpBitmapEncoder(RenderTargetBitmapをMemoryStreamに流し込む)
  • MemoryStream(編集前の画像が流れてる)
  • System.Drawing.Bitmap(MemoryStreamからBitmap作成)
  • Graphics(BitmapをGraphicsで編集)
  • MemoryStream(編集の画像が流れてる)
  • BitmapFrame(MemoryStreamからBitmapFrameを作成)
  • Image.Source(BitmapFrameを画面にセット)
  • →おわり

※画面上の画像に四角を書き込む、ということがこれでもできそう。
 できそうだが、画面上に四角を出したいなら、xaml上に<Canvas>とその中に<Rectangle/>を置いて、そのWidthやheight、Canvas.TopやCanvas.Bottomをプログラムで制御するほうが絶対いいなと思った。(テキストでも同様(<TextBlock>で。))

.cs
// 画面からRenderTargetBitmapに画像を描画
var canvas = new RenderTargetBitmap((int)MyImage.ActualWidth, (int)MyImage.ActualHeight, 96, 96, PixelFormats.Pbgra32);
canvas.Render(MyImage);

using (var stream = new MemoryStream())
{
    // MemoryStreamに、RenderTargetBitmapから画像を流し込む
    BitmapEncoder encoder = new BmpBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(canvas));
    encoder.Save(stream);

    // MemoryStreamからBitmapを作成
    var editted = new System.Drawing.Bitmap(stream);

    // GraphicsでBitmapを編集(四角を書き込む)
    using (var g = Graphics.FromImage(editted))
    {
        g.DrawRectangle(Pens.Green, new System.Drawing.Rectangle(3, 3, 200, 200));
    }
    // →この時点で、editted(Bitmap)に緑の四角が書き込まれてる
    // Streamはまだ。

    // 四角を書き込んだ画像をstreamに流し込む
    editted.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
    editted.Dispose();

    // MemoryStreamからBitmapを作成から、BitmapFrame(BitmapSourceの子クラス)を作成
    stream.Seek(0, SeekOrigin.Begin);// seekでBeginに戻さないと例外
    var bitmapSource = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
    // それをImageのSourceにセット → 表示!
    MyImage.Source = bitmapSource;
}

画像に別の画像を貼り付けて保存する(pngで透過も可)

ある画像に、別の画像を貼り付けて保存する。
貼り付ける側としては.jpg.bmp.pngなどが使えるが、
pngで透過部分がある画像だと、貼り付けるとその部分が透過されるの。
(ロゴマークとかを貼り付けるときに使えそう)

using (var bmp = new Bitmap(@"src.jpg"))            // 元の画像
{
    using (var g = Graphics.FromImage(bmp))         // 元の画像からGraphicsをつくる
    using (var pngbmp = new Bitmap("touka.png"))    // 貼り付けたい画像を開く(この場合はpngで、透明部分を透過できる)
    {
        // 貼り付ける画像を(5,5)の位置に描画する
        g.DrawImage(pngbmp, 5, 5, pngbmp.Width, pngbmp.Height);
    }
    // 保存する
    bmp.Save(@"output.bmp");
}

image.png

透過部分を含む.pngの作り方

余談だが、実験用の.pngファイルはエクセルを使って簡単に作れる。
https://amaotolog.com/excel/42
(エクセルのシートをhtmlとして保存したときの副産物的な保存のされ方)

参照

いろんな画像関連の型の変換
https://qiita.com/YSRKEN/items/a24bf2173f0129a5825c

これで画像に四角とか書き足す、できんか?(System.Drawingではないやつで)
https://docs.microsoft.com/ja-jp/dotnet/framework/wpf/graphics-multimedia/painting-with-images-drawings-and-visuals

しらべたいこと

PixelFormats.Pbgra32 ってなに?
https://water2litter.net/gin/?p=987 でやってるCreateで指定してるやつ。
よく出てくるがじつはわかってない。

3
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?