Bigdra.Log

備忘録

OpenCV for Unityで画像処理100本ノック 11~20

f:id:bigdra50:20210215145821p:plain

1~10はこちら

bigdra50.hatenablog.com

前回は画像処理部分をなるべく自分で実装していましたが,今回はライブラリ使っています.

リポジトリ

github.com

ライブラリ使うコードはOneHubdredKnock.A, なるべく使わず実装してるのはOneHundredKnock.Bに置きます.

Q11 平滑化フィルタ

f:id:bigdra50:20210215145903p:plain
平滑化フィルタ

using OpenCVForUnity.CoreModule;
using OpenCVForUnity.ImgprocModule;
using UnityEngine;

namespace OneHundredKnock.A
{
    /// <summary>
    /// 平滑化フィルタ
    /// フィルタ内の画素の平均値を出力するフィルタ
    /// </summary>
    public class Q11 : MonoBehaviour
    {
        private void Start()
        {
            var src = Util.LoadTexture("imori_256x256");
            var dst = new Mat();
            var size = 3;
            Imgproc.blur(src, dst, new Size(size, size));
            GetComponent<Renderer>().material.mainTexture = Util.MatToTexture2D(dst);
        }
    }
}

Q12 モーションフィルタ

f:id:bigdra50:20210215145949p:plain
モーションフィルタ
Imgproc.filter2Dの引数でカーネルを指定すれば任意のフィルタをかけられるようなので, これ以降のOpenCV側で用意されていない(または見つけられなかった)フィルターに関しては, カーネルの行列を入れ替えただけのコードになります.

using OpenCVForUnity.CoreModule;
using OpenCVForUnity.ImgprocModule;
using UnityEngine;

namespace OneHundredKnock.A
{
    /// <summary>
    /// モーションフィルタ
    /// 対角方向の平均値を取るフィルタ
    ///     |1/3 0  0|
    /// k = |0  1/3 0|
    ///     |0  0 1/3|
    /// </summary>
    public class Q12 : MonoBehaviour
    {
        private void Start()
        {
            var src = Util.LoadTexture("imori_256x256");
            var dst = new Mat();
            var kernel = new MatOfFloat(
                1f / 3f, 0f, 0f,
                0f, 1f / 3f, 0f,
                0f, 0f, 1f / 3f);
            Imgproc.filter2D(src, dst, -1, kernel);
            GetComponent<Renderer>().material.mainTexture = Util.MatToTexture2D(dst);
        }
    }
}

Q13 MAX-MINフィルタ

f:id:bigdra50:20210215150017p:plain
MAX-MINフィルタ

これはカーネルがわからなかったので自分で実装しました.

using OpenCVForUnity.CoreModule;
using OpenCVForUnity.ImgprocModule;
using UnityEngine;

namespace OneHundredKnock.B
{
    /// <summary>
    /// MAX-MINフィルタ
    /// フィルタ内の画素の最大最小の差を出力するフィルタで, エッジ検出のフィルタの一つ
    /// エッジ検出では多くの場合, グレースケール画像に対してフィルタリングを行う
    /// </summary>
    public class Q13 : MonoBehaviour
    {
        [SerializeField] private int _size = 3;

        private void Start()
        {
            var src = Util.LoadTexture("imori_256x256");
            var dst = new Mat(src.rows(), src.cols(), CvType.CV_8UC4);
            Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGB2GRAY);
            var edgeCols = new byte[dst.rows(), dst.cols()];

            for (var x = 0; x < dst.width(); x++)
            {
                for (var y = 0; y < dst.height(); y++)
                {
                    var max = byte.MinValue;
                    var min = byte.MaxValue;
                    for (var dx = -_size / 2; dx <= _size / 2; dx++)
                    {
                        for (var dy = -_size / 2; dy <= _size / 2; dy++)
                        {
                            if (x + dx >= 0 && x + dx < dst.width() && y + dy >= 0 && y + dy < dst.height())
                            {
                                var col = new byte[4];
                                dst.get(x + dx, y + dy, col);
                                if (col[0] > max) max = col[0];
                                if (col[0] < min) min = col[0];
                            }
                        }
                    }

                    edgeCols[x, y] = (byte) (max - min);
                }
            }

            for (var x = 0; x < dst.width(); x++)
            {
                for (var y = 0; y < dst.height(); y++)
                {
                    dst.put(x, y, new[]
                    {
                        edgeCols[x, y],
                        edgeCols[x, y],
                        edgeCols[x, y],
                        edgeCols[x, y],
                    });
                }
            }

            GetComponent<Renderer>().material.mainTexture = Util.MatToTexture2D(dst);
        }
    }
}

Q14 微分フィルタ

f:id:bigdra50:20210215150112p:plain
微分フィルタ

using OpenCVForUnity.CoreModule;
using OpenCVForUnity.ImgprocModule;
using UnityEngine;

namespace OneHundredKnock.A
{
    /// <summary>
    /// 微分フィルタ
    /// 輝度の急激な変化が起こっている部分のエッジを取り出すフィルタ
    /// 隣り合う画素同士の差を取る
    ///          |0 -1  0|           | 0  0  0|
    /// 縦: K =  |0  1  0|   横: K = |-1  1  0|
    ///         |0  0  0|           | 0  0  0|
    /// </summary>
    public class Q14 : MonoBehaviour
    {
        private void Start()
        {
            var src = Util.LoadTexture("imori_256x256");
            var dst = new Mat(src.rows(), src.cols(), CvType.CV_8UC4);
            var k_v = new[]
            {
                0f, -1f, 0f,
                0f, 1f, 0f,
                0f, 0f, 0f
            };
            var k_h = new[]
            {
                0f, 0f, 0f,
                -1f, 1f, 0f,
                0f, 0f, 0f
            };
            Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGBA2GRAY);
            Imgproc.filter2D(dst, dst, -1, new MatOfFloat(k_v));
            GetComponent<Renderer>().material.mainTexture = Util.MatToTexture2D(dst);
        }
    }
}

Q15 Prewittフィルタ

f:id:bigdra50:20210215150203p:plain
Prewittフィルタ

using OpenCVForUnity.CoreModule;
using OpenCVForUnity.ImgprocModule;
using UnityEngine;

namespace OneHundredKnock.A
{
    /// <summary>
    /// Prewittフィルタ
    /// エッジ抽出フィルタの1種
    /// 微分フィルタを3x3に拡大したもの
    ///          | 1  1  1|          | 1  0 -1|
    /// 縦: K =  | 0  0  0|   横: K = | 1  0 -1|
    ///         |-1  -1 -1|          | 1  0 -1|
    /// </summary>
    public class Q15 : MonoBehaviour
    {
        private void Start()
        {
            var src = Util.LoadTexture("imori_256x256");
            var dst = new Mat(src.rows(), src.cols(), CvType.CV_8UC4);
            var k_v = new[]
            {
                1f, 1f, 1f,
                0f, 0f, 0f,
                -1f, -1f, -1f
            };
            var k_h = new[]
            {
                1f, 0f, -1f,
                1f, 0f, -1f,
                1f, 0f, -1f
            };
            Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGBA2GRAY);
            Imgproc.filter2D(dst, dst, -1, new MatOfFloat(k_v));
            GetComponent<Renderer>().material.mainTexture = Util.MatToTexture2D(dst);
        }
    }
}

Q16 Sobelフィルタ

f:id:bigdra50:20210215150237p:plain
Sobelフィルタ

using OpenCVForUnity.CoreModule;
using OpenCVForUnity.ImgprocModule;
using UnityEngine;

namespace OneHundredKnock.A
{
    /// <summary>
    /// Sobelフィルタ
    /// エッジ抽出フィルタの1種
    /// prewittフィルタの中心部分に重みを付けたフィルタ
    ///          | 1  2  1|          | 1  0 -1|
    /// 縦: K =  | 0  0  0|   横: K = | 2  0 -2|
    ///         |-1  -2 -1|          | 1  0 -1|
    /// </summary>
    public class Q16 : MonoBehaviour
    {
        private void Start()
        {
            var src = Util.LoadTexture("imori_256x256");
            var dst = new Mat(src.rows(), src.cols(), CvType.CV_8UC4);
            var k_v = new[]
            {
                1f, 2f, 1f,
                0f, 0f, 0f,
                -1f, -2f, -1f
            };
            var k_h = new[]
            {
                1f, 0f, -1f,
                2f, 0f, -2f,
                1f, 0f, -1f
            };
            Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGBA2GRAY);
            Imgproc.filter2D(dst, dst, -1, new MatOfFloat(k_v));
            GetComponent<Renderer>().material.mainTexture = Util.MatToTexture2D(dst);
        }
    }
}

Q17 Lapcacianフィルタ

f:id:bigdra50:20210215150308p:plain
Laplacianフィルタ

using OpenCVForUnity.CoreModule;
using OpenCVForUnity.ImgprocModule;
using UnityEngine;

namespace OneHundredKnock.A
{
    /// <summary>
    /// Laplacianフィルタ
    /// エッジ抽出フィルタの1種
    /// 輝度の2次微分
    ///      | 0  1  0|
    /// K =  | 1 -4  1| 
    ///      | 0  1  0|
    /// </summary>
    public class Q17 : MonoBehaviour
    {
        private void Start()
        {
            var src = Util.LoadTexture("imori_256x256");
            var dst = new Mat(src.rows(), src.cols(), CvType.CV_8UC4);
            var k = new[]
            {
                0f, 1f, 0f,
                1f, -4f, 1f,
                0f, 1f, 0f
            };
            Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGBA2GRAY);
            Imgproc.filter2D(dst, dst, -1, new MatOfFloat(k));
            GetComponent<Renderer>().material.mainTexture = Util.MatToTexture2D(dst);
        }
    }
}

Q18 Embossフィルタ

f:id:bigdra50:20210215150343p:plain
Embossフィルタ

using OpenCVForUnity.CoreModule;
using OpenCVForUnity.ImgprocModule;
using UnityEngine;

namespace OneHundredKnock.A
{
    /// <summary>
    /// Embossフィルタ
    /// 輪郭部分を浮き出しにするフィルタ
    ///      |-2 -1  0|
    /// K =  |-1  1  1| 
    ///      | 0  1  2|
    /// </summary>
    public class Q18 : MonoBehaviour
    {
        private void Start()
        {
            var src = Util.LoadTexture("imori_256x256");
            var dst = new Mat(src.rows(), src.cols(), CvType.CV_8UC4);
            var k = new[]
            {
                -2f, -1f, 0f,
                -1f, 1f, 1f,
                0f, 1f, 2f
            };
            Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGBA2GRAY);
            Imgproc.filter2D(dst, dst, -1, new MatOfFloat(k));
            GetComponent<Renderer>().material.mainTexture = Util.MatToTexture2D(dst);
        }
    }
}

Q19 LoGフィルタ

f:id:bigdra50:20210215150421p:plain
LoG(Laplacian of Gaussian)フィルタ

using OpenCVForUnity.CoreModule;
using OpenCVForUnity.ImgprocModule;
using UnityEngine;

namespace OneHundredKnock.A
{
    /// <summary>
    /// LoG(Laplacian of Gaussian)フィルタ
    /// ガウシアンフィルタで平滑化した後にラプラシアンフィルタで輪郭を取り出すフィルタ
    /// Laplacianは2次微分をとるのでノイズが強調されるのを防ぐために, 予めGaussianでノイズを抑える
    /// 
    ///     |0  0  1  0  0|
    ///     |0  1  2  1  0|
    /// K = |1  2 -16 2  1|
    ///     |0  1  2  1  0|
    ///     |0  0  1  0  0|
    /// </summary>
    public class Q19 : MonoBehaviour
    {
        private void Start()
        {
            var src = Util.LoadTexture("imori_noise");
            var dst = new Mat(src.rows(), src.cols(), CvType.CV_8UC4);
            var k = new[]
            {
                0f, 0f, 1f, 0f, 0f,
                0f, 1f, 2f, 1f, 0f,
                1f, 2f, -16f, 2f, 1f,
                0f, 1f, 2f, 1f, 0f,
                0f, 0f, 1f, 0f, 0f,
            };
            Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGBA2GRAY);
            //Imgproc.GaussianBlur(gray, gauss, new Size(5,5), 3d);
            //Imgproc.Laplacian(gauss, dst, -1, 5);
            Imgproc.filter2D(dst, dst, -1, new MatOfFloat(k));
            GetComponent<Renderer>().material.mainTexture = Util.MatToTexture2D(dst);
        }
    }
}

Q20 ヒストグラム表示

OpenCV for Unityでは多分できないし必要性を感じないので省略

参考記事

Gasyori100knock/Question_11_20 at master · yoyoyo-yo/Gasyori100knock · GitHub 【Python/OpenCV】空間フィルタリングで平滑化・輪郭検出 | 西住工房 【画像処理】LoGフィルタの原理・特徴・計算式 | 西住工房