C#Opencvsharp4 realizes several image special effects

Environment configuration: VS2019, Winform, Opencvsharp4 4.5.5.20211231, .Net Framework 4.8

The original picture of the experiment:

1. Frosted glass special effects.

    Principle: Use the pixel value of a random point in the pixel area to replace the pixel of this point.

   Experimental results:

Main code:

        public static bool ImgGroundGlass(Mat src, out Mat dstImg, int ksize = 5)
        {
            dstImg = src.Clone();
            if (ksize < 3) return false;
            if (ksize % 2 != 1) return false;
            int a = ksize / 2;
            Random r = new Random((int)DateTime.Now.Ticks);
            int x = 0, y = 0;
            // 将图像划分成九个区域
            /*
            // 左上角 -- 上边 -- 右上角
            //   |        |        |
            //  左边  -- 中间 -- 右边
            //   |        |        |
            // 左下角 -- 下边 -- 右下角
            */
            // 每个区域进行分块讨论
            // 左上角
            for (int i = 0; i < a; i++)
            {
                for (int j = 0; j < a; j++)
                {
                    x = r.Next(0, a);
                    y = r.Next(0, a);
                    dstImg.At<Vec3b>(i, j) = src.At<Vec3b>(x, y);
                }
            }
            // 上边
            for (int i = 0; i < a; i++)
            {
                for (int j = a; j < src.Cols - a; j++)
                {
                    x = r.Next(0, a);
                    y = r.Next(-a, a);
                    dstImg.At<Vec3b>(i, j) = src.At<Vec3b>(x, y + j);
                }
            }
            // 右上角
            for (int i = 0; i < a; i++)
            {
                for (int j = src.Cols - a; j < src.Cols; j++)
                {
                    x = r.Next(0, a);
                    y = r.Next(src.Cols - a, src.Cols);
                    dstImg.At<Vec3b>(i, j) = src.At<Vec3b>(x, y);
                }
            }
            // 左边
            for (int i = a; i < src.Rows - a; i++)
            {
                for (int j = 0; j < a; j++)
                {
                    x = r.Next(-a, a);
                    y = r.Next(0, a);
                    dstImg.At<Vec3b>(i, j) = src.At<Vec3b>(x + i, y);
                }
            }
            // 中间
            for (int i = a; i < src.Rows - a; i++)
            {
                for (int j = a; j < src.Cols - a; j++)
                {
                    x = r.Next(-a, a);
                    y = r.Next(-a, a);
                    dstImg.At<Vec3b>(i, j) = src.At<Vec3b>(x + i, y + j);
                }
            }
            // 右边
            for (int i = a; i < src.Rows - a; i++)
            {
                for (int j = src.Cols - a; j < src.Cols; j++)
                {
                    x = r.Next(-a, a);
                    y = r.Next(src.Cols - a, src.Cols);
                    dstImg.At<Vec3b>(i, j) = src.At<Vec3b>(x + i, y);
                }
            }
            // 左下角 
            for (int i = src.Rows - a; i < src.Rows; i++)
            {
                for (int j = src.Cols - a; j < src.Cols; j++)
                {
                    x = r.Next(src.Rows - a, src.Rows);
                    y = r.Next(0, a);
                    dstImg.At<Vec3b>(i, j) = src.At<Vec3b>(x, y);
                }
            }
            // 下边
            for (int i = src.Rows - a; i < src.Rows; i++)
            {
                for (int j = a; j < src.Cols - a; j++)
                {
                    x = r.Next(src.Rows - a, src.Rows);
                    y = r.Next(-a, a);
                    dstImg.At<Vec3b>(i, j) = src.At<Vec3b>(x, y + j);
                }
            }
            // 右下角
            for (int i = src.Rows - a; i < src.Rows; i++)
            {
                for (int j = src.Cols - a; j < src.Cols; j++)
                {
                    x = r.Next(src.Rows - a, src.Rows);
                    y = r.Next(src.Cols - a, src.Cols);
                    dstImg.At<Vec3b>(i, j) = src.At<Vec3b>(x, y);
                }
            }
            return true;
        }

    When it comes to the pixel field, the boundary problem must be considered, that is, the problem that the array index exceeds the boundary. You can discuss various boundary issues like me separately, or set the upper and lower limits of the loop when traversing points, but this will cause some boundary points to not be processed, or change the size of the image, and only take the middle part that can be traversed, so There is no need to consider the border, but the output image becomes smaller.

2. Nostalgic effects

Principle: BGR channel image can use the following change formula

Realize the effect:

Main code:

   public static Mat ImgRetro(Mat src)
        {
            src.ConvertTo(src, MatType.CV_8UC3);
            Mat dstImg = new Mat(src.Size(), MatType.CV_8UC3);
            Vec3b vec = new Vec3b();
            float bb = 0, bg = 0, br = 0;
            for (int i = 0; i < src.Height; i++)
            {
                for (int j = 0; j < src.Width; j++)
                {
                    vec = src.At<Vec3b>(i, j);
                    bb = vec.Item2 * 0.272f + vec.Item1 * 0.534f + vec.Item0 * 0.131f;
                    bg = vec.Item2 * 0.349f + vec.Item1 * 0.686f + vec.Item0 * 0.168f;
                    br = vec.Item2 * 0.393f + vec.Item1 * 0.769f + vec.Item0 * 0.189f;
                    dstImg.At<Vec3b>(i, j) = new Vec3b(SetCorrectPix(bb), SetCorrectPix(bg), SetCorrectPix(br));
                }
            }
            return dstImg;
            byte SetCorrectPix(float byte1)
            {
                if (byte1 > 255)
                    return 255;
                else if (byte1 < 0)
                    return 0;
                return (byte)byte1;
            }
        }

Note that when changing the value of each corresponding pixel, its byte value should be converted into int double and other types, and then quantized to between 0 and 255 after transformation, and finally converted into byte. If you directly use the byte type to make changes, there may be overflow problems, resulting in wrong final results.

3. Sketch

Principle: You can follow the steps of PS sketch: 1. Convert the BGR image to grayscale 2. Invert the image 3. Gaussian filter 4. Lighten and darken the image

Experimental effect:

 Main code:

        public static Mat ImgSketch(Mat src, int meanV = 127)
        {
            Mat gray = new Mat();
            Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);

            Mat negGray = new Mat();
            Cv2.BitwiseNot(gray, negGray);

            Mat blurMat = new Mat();
            Cv2.GaussianBlur(negGray, blurMat, new OpenCvSharp.Size(3, 3), 0);

            Mat negBlurMat = new Mat();
            Cv2.BitwiseNot(blurMat, negBlurMat);

            Mat dstImg = new Mat();
            Cv2.Divide(gray, negBlurMat, dstImg, meanV);
            return dstImg;
        }

        My understanding is that after the image is filtered by Gaussian, the high-frequency part of it is filtered out, that is, the edge part of the image, that is to say, only the edge part of the filtered image has changed, and then use Cv2.Divide() to get As a result, only the changed part, that is, the edge part will be relatively large, and the other parts are basically 1. After proper zoom-in, only the edge part can be displayed. I've tried not negating twice and got similar results.

       Of course, we can also use the built-in Cv2.PencilSketch() to achieve sketch results, but the parameters are more difficult to adjust.

4. Embossed effects

Principle: The difference between each pixel and surrounding pixels, plus a certain pixel. When the point is in the smooth part, the pixel difference is very small, but in the boundary, the pixel difference is relatively large, so that the result will be more prominent in the boundary part, so as to achieve the effect of embossment.

Experimental results:

Main code:

        public static Mat ImgEmboss(Mat src, int kvalue = 150)
        {
            Mat dstImg = Mat.Zeros(src.Size(), MatType.CV_8UC1);

            Mat grayMat = new Mat();
            Cv2.CvtColor(src, grayMat, ColorConversionCodes.BGR2GRAY);
            int bb = 0;
            for (int i = 0; i < src.Height; i++)
            {
                for (int j = 0; j < src.Width - 1; j++)
                {
                    bb = grayMat.At<byte>(i, j) - grayMat.At<byte>(i, j + 1) + kvalue;
                    if (bb > 255)
                        bb = 255;
                    else if (bb < 0)
                        bb = 0;
                    dstImg.At<byte>(i, j) = (byte)bb;
                }
            }
            return dstImg;
        }

Also pay attention to the problem of index overrun and the conversion of byte values.

5. Aperture effect

Principle: Calculate the distance between each pixel and the center of the aperture, and increase the corresponding brightness according to the corresponding distance.

Experimental effect:

Main code:

        /// <summary>
        /// 光圈效果
        /// </summary>
        /// <param name="src">输入图像</param>
        /// <param name="radious">光圈半径</param>
        /// <param name="strength">光圈亮度</param>
        /// <param name="center">光圈中心点</param>
        /// <returns></returns>
        public static Mat ImgAperture(Mat src, int radious, int strength, OpenCvSharp.Point? center = null)
        {
            Mat dstImg = src.Clone();

            OpenCvSharp.Point cc = center ?? new OpenCvSharp.Point(src.Width / 2, src.Height / 2);

            bool en = true;
            en = en && cc.X > radious && cc.X + radious < src.Width;
            en = en && cc.Y > radious && cc.Y + radious < src.Height;
            en = en && cc.X > 0 && cc.X < src.Width;
            en = en && cc.Y > 0 && cc.Y < src.Height;
            if (en == false) return dstImg;

            float bb, bg, br;

            double dis = 0;
            int result = 0;

            for (int i = 0; i < src.Height; i++)
            {
                for (int j = 0; j < src.Width; j++)
                {
                    dis = Math.Pow(cc.X - j, 2) + Math.Pow(cc.Y - i, 2);

                    bb = src.At<Vec3b>(i, j).Item0;
                    bg = src.At<Vec3b>(i, j).Item1;
                    br = src.At<Vec3b>(i, j).Item2;

                    if (dis < radious * radious)
                    {
                        result = (int)(strength * (1 - Math.Sqrt(dis) / radious));
                        bb += result;
                        bg += result;
                        br += result;
                    }
                    dstImg.At<Vec3b>(i, j) = new Vec3b(SetCorrectPix(bb), SetCorrectPix(bg), SetCorrectPix(br));
                }
            }

            return dstImg;

            byte SetCorrectPix(float byte1)
            {
                if (byte1 > 255)
                    return 255;
                else if (byte1 < 0)
                    return 0;
                return (byte)byte1;
            }
        }

6. Image squeeze and distorting mirror effects

Principle: The essence is the non-linear transformation of image coordinates. For specific steps, please refer to: OpenCV3 Introduction (14) Image Effects - Squeeze, Distorting Mirror, Distortion - Ah Ha Peng - Blog Garden

The result display:

image squeeze

 distorting mirror

code:

        // 挤压
        public static void ImgPinch(Mat src, out Mat dstImg, int degree = 5, OpenCvSharp.Point? center = null)
        {
            if (degree < 1) degree = 1;
            if (degree > 32) degree = 32;

            src.ConvertTo(src, MatType.CV_8UC3);
            dstImg = Mat.Zeros(src.Size(), MatType.CV_8UC3);

            OpenCvSharp.Point cc = center ?? new OpenCvSharp.Point(src.Width / 2, src.Height / 2);

            int X, Y, offsetX, offsetY;
            double radian, radius;  //弧和半径

            for (int i = 0; i < src.Rows; i++)
            {
                for (int j = 0; j < src.Cols; j++)
                {
                    offsetX = j - cc.X;
                    offsetY = i - cc.Y;

                    radian = Math.Atan2((double)offsetY, (double)offsetX);

                    // 半径
                    radius = Math.Sqrt((float)(offsetX * offsetX + offsetY * offsetY));
                    radius = Math.Sqrt(radius) * degree;

                    X = (int)(radius * Math.Cos(radian)) + cc.X;
                    Y = (int)(radius * Math.Sin(radian)) + cc.Y;

                    if (X < 0) X = 0;
                    if (X >= src.Cols) X = src.Cols - 1;
                    if (Y < 0) Y = 0;
                    if (Y >= src.Rows) Y = src.Rows - 1;

                    dstImg.At<Vec3b>(i, j) = src.At<Vec3b>(Y, X);
                }
            }
        }



        // 哈哈镜
        public static void ImgDistoringMirror(Mat src, out Mat dst, int degree = 100, OpenCvSharp.Point? center = null, int R = 100)
        {
            dst = Mat.Zeros(src.Size(), MatType.CV_8UC3);

            OpenCvSharp.Point cc = center ?? new OpenCvSharp.Point(src.Width / 2, src.Height / 2);

            bool en = true;
            en = en && cc.X > R && cc.X + R < src.Width;
            en = en && cc.Y > R && cc.Y + R < src.Height;
            en = en && cc.X > 0 && cc.X < src.Width;
            en = en && cc.Y > 0 && cc.Y < src.Height;
            if (en == false) return;

            int i, j;
            int X, Y, offsetX, offsetY;
            double radian, radius;  //弧和半径

            for (i = 0; i < src.Rows; i++)
            {
                for (j = 0; j < src.Cols; j++)
                {
                    offsetX = j - cc.X;
                    offsetY = i - cc.Y;
                    radian = Math.Atan2((double)offsetY, (double)offsetX);

                    // 半径
                    radius = Math.Sqrt((float)(offsetX * offsetX + offsetY * offsetY));
                    if (radius <= R && radius > 1)
                    {
                        float k = (float)(Math.Sqrt(radius / R) * radius / R * degree);
                        X = (int)(Math.Cos(radian) * k) + cc.X;
                        Y = (int)(Math.Sin(radian) * k) + cc.Y;

                        if (X < 0) X = 0;
                        if (X >= src.Cols) X = src.Cols - 1;
                        if (Y < 0) Y = 0;
                        if (Y >= src.Rows) Y = src.Rows - 1;
                        dst.At<Vec3b>(i, j) = src.At<Vec3b>(Y, X);
                    }
                    else
                    {
                        dst.At<Vec3b>(i, j) = src.At<Vec3b>(i, j);
                    }
                }
            }
        }

 7. Rainbow effects

Principle: It is to blend the painted rainbow image into the image

result:

Main code:

    

        // 在指定矩形内生成彩虹
        public static Mat DrawRainBow(OpenCvSharp.Size size, OpenCvSharp.Rect rect)
        {
            Mat dstImg = Mat.Zeros(size, MatType.CV_8UC3);

            Cv2.Ellipse(dstImg, new OpenCvSharp.Point(rect.Width / 2, rect.Height * 4 / 5), new OpenCvSharp.Size(rect.Width / 2 + 100, rect.Height * 4 / 5), 180, 0, 180, new Scalar(255, 0, 128), 5);
            Cv2.Ellipse(dstImg, new OpenCvSharp.Point(rect.Width / 2, rect.Height * 4 / 5), new OpenCvSharp.Size(rect.Width / 2 - 8 + 100, rect.Height * 4 / 5 - 8), 180, 0, 180, new Scalar(255, 0, 0), 5);
            Cv2.Ellipse(dstImg, new OpenCvSharp.Point(rect.Width / 2, rect.Height * 4 / 5), new OpenCvSharp.Size(rect.Width / 2 - 8 * 2 + 100, rect.Height * 4 / 5 - 8 * 2), 180, 0, 180, new Scalar(255, 255, 0), 5);
            Cv2.Ellipse(dstImg, new OpenCvSharp.Point(rect.Width / 2, rect.Height * 4 / 5), new OpenCvSharp.Size(rect.Width / 2 - 8 * 3 + 100, rect.Height * 4 / 5 - 8 * 3), 180, 0, 180, new Scalar(0, 255, 0), 5);
            Cv2.Ellipse(dstImg, new OpenCvSharp.Point(rect.Width / 2, rect.Height * 4 / 5), new OpenCvSharp.Size(rect.Width / 2 - 8 * 4 + 100, rect.Height * 4 / 5 - 8 * 4), 180, 0, 180, new Scalar(0, 255, 255), 5);
            Cv2.Ellipse(dstImg, new OpenCvSharp.Point(rect.Width / 2, rect.Height * 4 / 5), new OpenCvSharp.Size(rect.Width / 2 - 8 * 5 + 100, rect.Height * 4 / 5 - 8 * 5), 180, 0, 180, new Scalar(0, 128, 255), 5);
            Cv2.Ellipse(dstImg, new OpenCvSharp.Point(rect.Width / 2, rect.Height * 4 / 5), new OpenCvSharp.Size(rect.Width / 2 - 8 * 6 + 100, rect.Height * 4 / 5 - 8 * 6), 180, 0, 180, new Scalar(0, 0, 255), 5);

            Cv2.GaussianBlur(dstImg, dstImg, new OpenCvSharp.Size(7, 7), 0, 0);
            return dstImg;
        }
  
  public static void ImgRainBow(Mat src, out Mat dst, Rect rect)
        {
            src.ConvertTo(src, MatType.CV_8UC3);
            dst = src.Clone();
            Mat rainbow = DrawRainBow(rect.Size, rect);

            int x = (rect.Left + rect.Right) / 2;
            int y = (rect.Top + rect.Bottom) / 2;
            OpenCvSharp.Point p = new OpenCvSharp.Point(x, y);
            Cv2.SeamlessClone(rainbow, src, null, p, dst, SeamlessCloneMethods.MixedClone);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="src"></param>
        /// <param name="dst"></param>
        /// <param name="rect"></param>
        /// <param name="vv">彩虹亮度</param>
        public static void ImgRainBow_AddWeight(Mat src, out Mat dst, Rect rect, double vv = 0.2)
        {
            src.ConvertTo(src, MatType.CV_8UC3);
            dst = src.Clone();
            Mat rainbow = DrawRainBow(src.Size(), rect);
            Cv2.AddWeighted(src, 1.0, rainbow, vv, 0, dst);
        }

       The implementation and results of different fusion methods are different. I wrote two kinds of Cv2.SeamlessClone() and Cv2.AddWeighted(). Pay attention to the input image requirements of the two methods.

 These are the basic effects of several common image effects, and there are other welcome additions, learn together and make progress together

 

 

Guess you like

Origin blog.csdn.net/Iawfy_/article/details/124704693