SKProgramLab

Let's Enjoy Programming! ~画像処理/IoT/機械学習など~

C#/VB.NETで画像処理⑫<画像の指定色抽出>

こんにちは、SKです。
f:id:SKProgramLab:20200310203357p:plain:w100

C#/VB.NETで画像処理シリーズの第12弾。
カメラ画像のピクセルにアクセスし、指定した色のピクセルのみ抽出する方法を紹介します。

PictureBox上でマウスをクリックすると、その座標のR,G,B情報を取得し、
カメラ画像内で指定した色に近いピクセルを抜き出し、別のMat画像として表示します。

OpenCVSharpとは?

skprogramlab.hatenablog.com

(前回)画像のピクセルアクセス

skprogramlab.hatenablog.com

動画手順

youtu.be

①色表示Labelを配置する

フォーム上にLabelを配置します。
前回の記事で作成したラベルをそのまま使ってOKです。
f:id:SKProgramLab:20200416214115p:plain

②PictureBoxのMouseDownイベントを作成する

PictureBox上のマウスクリック座標取得には、MouseDownイベントを使用します。
PictureBoxのプロパティ→"イベント"(⚡アイコン)を選択→MouseDownの欄をダブルクリックし、コード上にMouseDownイベントを作成します。
f:id:SKProgramLab:20200417232909p:plain

f:id:SKProgramLab:20200417233029p:plain

指定色を格納するScalar型の変数cursorColorを定義し、MouseDownイベント内で下記のようにマウス座標の色取得とラベル表示を行います。

PictureBox画像をBitmapに変換 ➡ GetPixelで指定座標の色取得 ➡ 変数に格納+ラベルにテキスト表示

private Scalar cursorColor;

private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
    Bitmap bmp = (Bitmap)pictureBox1.Image;
    Color c = bmp.GetPixel(e.X, e.Y);
    cursorColor = new Scalar(c.B, c.G, c.R);
    label1.Text = "R:" + c.R + ",G:" + c.G + ",B:" + c.B;
    label1.BackColor = c;
}


③指定色ピクセルを抽出する

前回同様、Mat画像のピクセルアクセスにはMatIndexerを使用します。
今回は元画像と抽出画像で2種類のIndexerを定義します。

二重Forループで各ピクセルの色情報を取得し、②の指定色のR,G,Bの値が全て近い(差が20以下の)ピクセルのみ抜き出し、その色を抽出画像のMatに書き込んでいきます。

private void Process(Mat img)        
{
    //元画像アクセス用
    var mat3_src = new Mat<Vec3b>(img);
    var indexer_src = mat3_src.GetIndexer();

    //抽出画像アクセス用
    Mat pixelImage = new Mat(img.Size(), MatType.CV_8UC3, Scalar.Black);
    var mat3_dst = new Mat<Vec3b>(pixelImage);
    var indexer_dst = mat3_dst.GetIndexer();

    //ピクセルアクセス
    for (int y = 0; y < img.Height; y++)
    {
        for (int x = 0; x < img.Width; x++)
        {
            Vec3b pixel = indexer_src[y, x];
            if(Math.Abs(pixel.Item0 - cursorColor.Val0) <= 20 && 
               Math.Abs(pixel.Item1 - cursorColor.Val1) <= 20 && 
               Math.Abs(pixel.Item2 - cursorColor.Val2) <= 20)
            {
                indexer_dst[y, x] = pixel;
            }
        }
    }
    Cv2.ImShow("pixel", pixelImage);
    Cv2.WaitKey(1);
}

Math.Abs関数は、差の絶対値を計算する関数です。

実行します。
白っぽいところ
f:id:SKProgramLab:20200417235020p:plain

灰色っぽいところ
f:id:SKProgramLab:20200417235152p:plain

赤っぽいところ
f:id:SKProgramLab:20200417235348p:plain

最後に、コード全文を載せておきます。
C#

using System;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                using (VideoCapture v = new VideoCapture(0))
                using (Mat img = new Mat())
                {
                    while (true)
                    {
                        v.Read(img);
                        Process(img);
                        pictureBox1.Image = BitmapConverter.ToBitmap(img);
                    }
                }
            });
        }

        private Scalar cursorColor;

        private void Process(Mat img)        
        {
            //元画像アクセス用
            var mat3_src = new Mat<Vec3b>(img);
            var indexer_src = mat3_src.GetIndexer();

            //抽出画像アクセス用
            Mat pixelImage = new Mat(img.Size(), MatType.CV_8UC3, Scalar.Black);
            var mat3_dst = new Mat<Vec3b>(pixelImage);
            var indexer_dst = mat3_dst.GetIndexer();

            //ピクセルアクセス
            for (int y = 0; y < img.Height; y++)
            {
                for (int x = 0; x < img.Width; x++)
                {
                    Vec3b pixel = indexer_src[y, x];
                    if(Math.Abs(pixel.Item0 - cursorColor.Val0) <= 20 && 
                       Math.Abs(pixel.Item1 - cursorColor.Val1) <= 20 && 
                       Math.Abs(pixel.Item2 - cursorColor.Val2) <= 20)
                    {
                        indexer_dst[y, x] = pixel;
                    }
                }
            }
            Cv2.ImShow("pixel", pixelImage);
            Cv2.WaitKey(1);
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            Bitmap bmp = (Bitmap)pictureBox1.Image;
            Color c = bmp.GetPixel(e.X, e.Y);
            cursorColor = new Scalar(c.B, c.G, c.R);
            label1.Text = "R:" + c.R + ",G:" + c.G + ",B:" + c.B;
            label1.BackColor = c;
        }
    }
}


VB.NET

Imports OpenCvSharp
Imports OpenCvSharp.Extensions

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Task.Run(Sub()
                     Using v As New VideoCapture(0)
                         Using img As New Mat
                             Do
                                 v.Read(img)
                                 Process(img)
                                 PictureBox1.Image = img.ToBitmap
                             Loop
                         End Using
                     End Using
                 End Sub)
    End Sub

    Private cursorColor As Scalar = Nothing

    Private Sub Process(ByVal img As Mat)

        '元画像アクセス用
        Dim indexer_src As MatIndexer(Of Vec3b) = img.GetGenericIndexer(Of Vec3b)()

        '抽出画像アクセス用
        Dim pixelImage As New Mat(img.Size, MatType.CV_8UC3, Scalar.Black)
        Dim indexer_dst As MatIndexer(Of Vec3b) = pixelImage.GetGenericIndexer(Of Vec3b)()

        'ピクセルアクセス
        For y = 0 To img.Height
            For x = 0 To img.Width
                Dim pixel As Vec3b = indexer_src(y, x)
                If Math.Abs(pixel.Item0 - cursorColor.Val0) <= 20 AndAlso
                   Math.Abs(pixel.Item1 - cursorColor.Val1) <= 20 AndAlso
                   Math.Abs(pixel.Item2 - cursorColor.Val2) <= 20 Then
                    indexer_dst(y, x) = pixel
                End If
            Next
        Next
        Cv2.ImShow("pixel", pixelImage)
        Cv2.WaitKey(1)
    End Sub

    Private Sub PictureBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseDown
        Dim bmp As Bitmap = PictureBox1.Image
        Dim c As Color = bmp.GetPixel(e.X, e.Y)
        cursorColor = New Scalar(c.B, c.G, c.R)
        Label1.Text = "R:" & c.R & ",G:" & c.G & ",B:" & c.B
        Label1.BackColor = c
    End Sub

End Class