LoginSignup
0
1

More than 3 years have passed since last update.

ベクトルスコープ

Last updated at Posted at 2020-07-10
using System;
using System.Linq;
using OpenCvSharp;

public static class VectorScope
{
    const double RY_R = +0.701;
    const double RY_G = -0.587;
    const double RY_B = -0.114;

    const double BY_R = -0.299;
    const double BY_G = -0.587;
    const double BY_B = +0.886;

    public static Point Rotation(int cx, int cy, double theta, double radius)
    {
        var x = cx + radius * Math.Cos(theta);
        var y = cy - radius * Math.Sin(theta);
        return new Point(Math.Round(x, MidpointRounding.AwayFromZero), Math.Round(y, MidpointRounding.AwayFromZero));
    }

    public static (double ry, double by, double ec, double maxval) Calc(Scalar bgr)
    {
        var ry = RY_R * bgr[2] + RY_G * bgr[1] + RY_B * bgr[0];
        var by = BY_R * bgr[2] + BY_G * bgr[1] + BY_B * bgr[0];
        var ec = Math.Sqrt(Math.Pow(ry / 1.14, 2) + Math.Pow(by / 2.03, 2));
        var maxval = Math.Sqrt(Math.Pow(RY_R / 1.14, 2) + Math.Pow(BY_B / 2.03, 2));
        return (ry, by, ec, maxval);
    }

    public static Point Rgb2Point(int cx, int cy, double radius, Scalar bgr)
    {
        var (ry, by, ec, maxval) = Calc(bgr);
        var theta = Math.Atan2(ry / 1.14, by / 2.03);
        return Rotation(cx, cy, theta, ec / maxval * radius);
    }

    public static void DrawTarget(Mat img, int cx, int cy, int radius, Scalar bgr, double angle, string element)
    {
        var (_, _, ec, maxval) = Calc(bgr);
        var percent = ec / maxval;

        {
            var coeff1 = 0.40;
            var radius1 = radius * percent * (1 - coeff1 / 2);
            var radius2 = radius * percent * (1 + coeff1 / 2);

            var angle1 = Math.Round(angle - 10.0, MidpointRounding.AwayFromZero);
            var angle2 = angle1 + (10.0 * 2);

            var coeff2 = 0.28;
            var radius3 = radius * percent * (1 - coeff2 / 2);
            var radius4 = radius * percent * (1 + coeff2 / 2);

            var p1 = Rotation(cx, cy, angle1 * Math.PI / 180, radius1);
            var p2 = Rotation(cx, cy, angle1 * Math.PI / 180, radius2);

            var p3 = Rotation(cx, cy, angle1 * Math.PI / 180, radius3);
            var p4 = Rotation(cx, cy, angle1 * Math.PI / 180, radius4);
            Cv2.Line(img, p1, p3, Scalar.LightGray, 1, LineTypes.AntiAlias);
            Cv2.Line(img, p4, p2, Scalar.LightGray, 1, LineTypes.AntiAlias);

            var p5 = Rotation(cx, cy, angle2 * Math.PI / 180, radius1);
            var p6 = Rotation(cx, cy, angle2 * Math.PI / 180, radius2);

            var p7 = Rotation(cx, cy, angle2 * Math.PI / 180, radius3);
            var p8 = Rotation(cx, cy, angle2 * Math.PI / 180, radius4);
            Cv2.Line(img, p5, p7, Scalar.LightGray, 1, LineTypes.AntiAlias);
            Cv2.Line(img, p8, p6, Scalar.LightGray, 1, LineTypes.AntiAlias);

            Cv2.Ellipse(img, new Point(cx, cy), new Size(radius1, radius1), -angle2, 0, 5.0, Scalar.LightGray, 1, LineTypes.AntiAlias);
            Cv2.Ellipse(img, new Point(cx, cy), new Size(radius2, radius2), -angle2, 0, 5.0, Scalar.LightGray, 1, LineTypes.AntiAlias);

            Cv2.Ellipse(img, new Point(cx, cy), new Size(radius1, radius1), -angle2, 15, 20, Scalar.LightGray, 1, LineTypes.AntiAlias);
            Cv2.Ellipse(img, new Point(cx, cy), new Size(radius2, radius2), -angle2, 15, 20, Scalar.LightGray, 1, LineTypes.AntiAlias);
        }

        {
            var coeff1 = (0.714 * 5 / 100) / maxval; // 5IRE
            var radius1 = radius * percent * (1 - coeff1 / 2);
            var radius2 = radius * percent * (1 + coeff1 / 2);

            var angle1 = Math.Round(angle - 2.5, MidpointRounding.AwayFromZero);
            var angle2 = angle1 + (2.5 * 2);

            var p1 = Rotation(cx, cy, angle1 * Math.PI / 180, radius1);
            var p2 = Rotation(cx, cy, angle1 * Math.PI / 180, radius2);
            Cv2.Line(img, p1, p2, Scalar.LightGray, 1, LineTypes.AntiAlias);

            var p3 = Rotation(cx, cy, angle2 * Math.PI / 180, radius1);
            var p4 = Rotation(cx, cy, angle2 * Math.PI / 180, radius2);
            Cv2.Line(img, p3, p4, Scalar.LightGray, 1, LineTypes.AntiAlias);

            Cv2.Ellipse(img, new Point(cx, cy), new Size(radius1, radius1), -angle2, 0, 5, Scalar.LightGray, 1, LineTypes.AntiAlias);
            Cv2.Ellipse(img, new Point(cx, cy), new Size(radius2, radius2), -angle2, 0, 5, Scalar.LightGray, 1, LineTypes.AntiAlias);
        }

        {
            var radius1 = radius * percent;
            var p1 = Rotation(cx, cy, angle * Math.PI / 180, radius1);

            Cv2.PutText(img, element, new Point(p1.X, p1.Y - 18), HersheyFonts.HersheySimplex, 0.8, Scalar.LightGray, 1, LineTypes.AntiAlias);
        }
    }

    public static void DrawBackgroud(Mat img, int cx, int cy, int radius)
    {
        foreach (var i in Enumerable.Range(1, 5).Select(n => (1 / 5.0 * n)))
        {
            Cv2.Circle(img, cx, cy, (int)(radius * i), Scalar.DimGray, (i == 1) ? 2 : 1, LineTypes.AntiAlias);
        }

        foreach (var i in Enumerable.Range(0, 4).Select(n => n * 360 / 4))
        {
            Point p2;
            p2 = Rotation(cx, cy, i * Math.PI / 180, radius);
            Cv2.Line(img, cx, cy, p2.X, p2.Y, Scalar.DimGray, 1, LineTypes.AntiAlias);

            // +Q(33)/-I(303)
            p2 = Rotation(cx, cy, (i + 33.0) * Math.PI / 180, radius * 0.88);
            Cv2.Line(img, cx, cy, p2.X, p2.Y, Scalar.DimGray, 1, LineTypes.AntiAlias);
        }

        foreach (var i in Enumerable.Range(0, 180).Select(n => n * 2))
        {
            var p1 = Rotation(cx, cy, i * Math.PI / 180, radius * ((i % 10) == 0 ? 0.940 : 0.960));
            var p2 = Rotation(cx, cy, i * Math.PI / 180, radius);
            Cv2.Line(img, p1, p2, Scalar.DimGray, (i % 10 == 0) ? 2 : 1, LineTypes.AntiAlias);
        }

        // M(60.8)/R(103.4)/Y(167.1)/G(240.8)/C(283.4)/B(347.1)
        Scalar[] BGR = {
            new Scalar(1.0, 0.0, 1.0),
            new Scalar(0.0, 0.0, 1.0),
            new Scalar(0.0, 1.0, 1.0),
            new Scalar(0.0, 1.0, 0.0),
            new Scalar(1.0, 1.0, 0.0),
            new Scalar(1.0, 0.0, 0.0)
        };

        double[] ANGLE = { 60.8, 103.4, 167.1, 240.8, 283.4, 347.1 };

        string[] ELEMENT = { "MG", "R", "YL", "G", "CY", "B" };

        foreach (var ((bgr, angle), element) in BGR.Zip(ANGLE, Tuple.Create).Zip(ELEMENT, Tuple.Create))
        {
            DrawTarget(img, cx, cy, radius, bgr, angle, element);
        }
    }

    public static void DrawPixels(Mat img, int cx, int cy, int radius, Scalar bgr)
    {
        var point = Rgb2Point(cx, cy, radius, bgr);
        var bgr_ = new Scalar(bgr[0] * 255, bgr[1] * 255, bgr[2] * 255);
        Cv2.Circle(img, point, 1, bgr_, -1, LineTypes.AntiAlias);
    }

    public static void Run()
    {
        const int COLS = 640;
        const int ROWS = 640;
        const int MARGIN = 10;

        var image = Cv2.ImRead("Mandrill.bmp");

        var cx = COLS / 2;
        var cy = ROWS / 2;
        var radius = cy - MARGIN;
        var image_bg = new Mat<Vec3b>(ROWS, COLS, Scalar.Black);

        var data = new Vec3d[image.Height, image.Width];
        image.ConvertTo(InputArray.Create(data).GetMat(), MatType.CV_64FC3, 1 / 255.0, 0);

        DrawBackgroud(image_bg, cx, cy, radius);

        var image_dst = image_bg.Clone();

        foreach (var bgr in data.Cast<Vec3d>())
        {
            DrawPixels(image_dst, cx, cy, radius, (Scalar)bgr);
        }

        Cv2.AddWeighted(image_dst, 1.0, image_bg, 0.4, 0.0, image_dst);

        Cv2.ImShow("input", image);
        Cv2.ImShow("output", image_dst);
        Cv2.WaitKey(0);
    }
}

image.png
image.png

・以下のサイトを参考にさせていただきました。
ベクトルスコープを描いてみる @linear_pcm0153

0
1
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
0
1