【问题标题】:Creating vignette filter in opencv?在opencv中创建小插图过滤器?
【发布时间】:2014-05-04 11:42:15
【问题描述】:

我们如何在opencv中制作小插图过滤器?我们需要为它实现任何算法还是只使用 BGR 的值?我们如何制作这种类型的过滤器。我看到了它的实现here,但我没有清楚地理解它。任何有完整算法指导和实施指导的人都会受到高度评价。

Abid rehman K 回答后我在 c++ 中尝试过这个

int main()
{
    Mat v;
    Mat img = imread ("D:\\2.jpg");
    img.convertTo(v, CV_32F);
    Mat a,b,c,d,e;
    c.create(img.rows,img.cols,CV_32F);
    d.create(img.rows,img.cols,CV_32F);
    e.create(img.rows,img.cols,CV_32F);

    a = getGaussianKernel(img.cols,300,CV_32F);

    b = getGaussianKernel(img.rows,300,CV_32F);


    c = b*a.t();

    double minVal;     
    double maxVal;          
    cv::minMaxLoc(c, &minVal, &maxVal);

        d = c/maxVal;
    e = v*d ;        // This line causing error
    imshow ("venyiet" , e);
    cvWaitKey();
}

d 显示正确,但 e=v*d 行导致运行时错误

OpenCV Error: Assertion failed (type == B.type() && (type == CV_32FC1 || type ==
CV_64FC1 || type == CV_32FC2 || type == CV_64FC2)) in unknown function, file ..
\..\..\src\opencv\modules\core\src\matmul.cpp, line 711

【问题讨论】:

  • 你没有做 c=b*a.T 函数。
  • 我是不是忘记在这里上传了,实际上我上传了d图片,它显示了没有图片的小插曲
  • @Ahmad 不是不想帮你,而是阿比德的回答已经够好了。
  • 是的,毫无疑问,它的答案令人印象深刻,但我正在尝试我的 C++ 工作,而我只是遇到了障碍
  • 问题是vd都是CV_32F,这不是该操作支持的格式。

标签: c++ opencv image-processing filter computer-vision


【解决方案1】:

您可以使用 OpenCV 中提供的高斯内核进行简单的实现。

  1. 加载图片,获取其行数和列数
  2. 创建两个大小为行和列的高斯核,例如 A、B。其差异取决于您的需求。
  3. C = transpose(A)*B,即将列向量与行向量相乘,结果数组的大小应与图像的大小相同。
  4. D = C/C.max()
  5. E = img*D

参见下面的实现(对于灰度图像):

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('temp.jpg',0)
row,cols = img.shape

a = cv2.getGaussianKernel(cols,300)
b = cv2.getGaussianKernel(rows,300)
c = b*a.T
d = c/c.max()
e = img*d

cv2.imwrite('vig2.png',e)

下面是我的结果:

彩色图像也是如此:

注意:当然,它是居中的。您需要进行额外的修改才能将焦点移到其他位置。

【讨论】:

  • 你能给我演示一下 c++ 吗?我正在尝试转换它,但它向我显示错误
  • max()的作用是什么
  • max() 找到图像中的最大值。今晚我将尝试使用 C++。但这很简单。您可以在 docs.opencv.org 的帮助下完成。
  • C++ 中是否允许使用 A*B?没有类似 cv::multiply 的东西吗?
  • @Ahmad 编辑问题中的代码,分享您的 C++ 代码,然后通知您收到的错误消息。不要忘记告诉我们导致错误的行。
【解决方案2】:

首先,Abid Rahman K 描述了使用此过滤器的最简单方法。您应该花时间和注意力认真研究他的答案。 维基百科对 Vignetting 的看法对于那些从未听说过此过滤器的人来说也很清楚。

Browny'sthis filter 的实现要复杂得多。但是,我将他的代码移植到 C++ API 并对其进行了简化,以便您可以自己按照说明进行操作。

#include <math.h>

#include <vector>

#include <cv.hpp>
#include <highgui/highgui.hpp>


// Helper function to calculate the distance between 2 points.
double dist(CvPoint a, CvPoint b)
{
    return sqrt(pow((double) (a.x - b.x), 2) + pow((double) (a.y - b.y), 2));
}

// Helper function that computes the longest distance from the edge to the center point.
double getMaxDisFromCorners(const cv::Size& imgSize, const cv::Point& center)
{
    // given a rect and a line
    // get which corner of rect is farthest from the line

    std::vector<cv::Point> corners(4);
    corners[0] = cv::Point(0, 0);
    corners[1] = cv::Point(imgSize.width, 0);
    corners[2] = cv::Point(0, imgSize.height);
    corners[3] = cv::Point(imgSize.width, imgSize.height);

    double maxDis = 0;
    for (int i = 0; i < 4; ++i)
    {
        double dis = dist(corners[i], center);
        if (maxDis < dis)
            maxDis = dis;
    }

    return maxDis;
}

// Helper function that creates a gradient image.   
// firstPt, radius and power, are variables that control the artistic effect of the filter.
void generateGradient(cv::Mat& mask)
{
    cv::Point firstPt = cv::Point(mask.size().width/2, mask.size().height/2);
    double radius = 1.0;
    double power = 0.8;

    double maxImageRad = radius * getMaxDisFromCorners(mask.size(), firstPt);

    mask.setTo(cv::Scalar(1));
    for (int i = 0; i < mask.rows; i++)
    {
        for (int j = 0; j < mask.cols; j++)
        {
            double temp = dist(firstPt, cv::Point(j, i)) / maxImageRad;
            temp = temp * power;
            double temp_s = pow(cos(temp), 4);
            mask.at<double>(i, j) = temp_s;
        }
    }
}

// This is where the fun starts!
int main()
{
    cv::Mat img = cv::imread("stack-exchange-chefs.jpg");
    if (img.empty())
    {
        std::cout << "!!! Failed imread\n";
        return -1;
    }

    /*
    cv::namedWindow("Original", cv::WINDOW_NORMAL);
    cv::resizeWindow("Original", img.size().width/2, img.size().height/2);
    cv::imshow("Original", img);
    */

img 的样子:

    cv::Mat maskImg(img.size(), CV_64F);
    generateGradient(maskImg);

    /*
    cv::Mat gradient;
    cv::normalize(maskImg, gradient, 0, 255, CV_MINMAX);
    cv::imwrite("gradient.png", gradient);
    */

ma​​skImg 的样子:

    cv::Mat labImg(img.size(), CV_8UC3);
    cv::cvtColor(img, labImg, CV_BGR2Lab);

    for (int row = 0; row < labImg.size().height; row++)
    {
        for (int col = 0; col < labImg.size().width; col++)
        {
            cv::Vec3b value = labImg.at<cv::Vec3b>(row, col);
            value.val[0] *= maskImg.at<double>(row, col);
            labImg.at<cv::Vec3b>(row, col) =  value;
        }
    }

    cv::Mat output;
    cv::cvtColor(labImg, output, CV_Lab2BGR);
    //cv::imwrite("vignette.png", output);

    cv::namedWindow("Vignette", cv::WINDOW_NORMAL);
    cv::resizeWindow("Vignette", output.size().width/2, output.size().height/2);
    cv::imshow("Vignette", output);
    cv::waitKey();

    return 0;
}

输出是什么样的:

如上面代码中所述,通过改变firstPtradiuspower的值可以实现更强/更弱的艺术效果。

祝你好运!

【讨论】:

  • +1 - 哈哈...谢谢卡尔...你减少了我的工作。在我的笔记本电脑最近格式化之后,我不得不再次设置所有 C++ 环境(现在太忙太懒了)。
  • @karlphillip 我喜欢你对 SO 的每一个回答,解释得很好,格式也很好,但不好的是我总是需要向你致意:P 很好,谢谢
  • 哦...首先我以为这张图片来自某部电影。但他们是stackexchange的厨师,对吧?她看起来像是要打败某人:P
  • 呵呵 :) 这张图真有趣!
  • 不错的答案+1,但为什么我们需要找到which corner of rect is farthest from the line
【解决方案3】:

这是灰度图像渐晕的 C++ 实现

#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>

using namespace cv;
using namespace std;

int main(int argv, char** argc)
 {
     Mat test = imread("test.jpg", IMREAD_GRAYSCALE);

     Mat kernel_X = getGaussianKernel(test.cols, 100);
     Mat kernel_Y = getGaussianKernel(test.rows, 100);
     Mat kernel_X_transpose;
     transpose(kernel_X, kernel_X_transpose);
     Mat kernel = kernel_Y * kernel_X_transpose;

     Mat mask_v, proc_img;
     normalize(kernel, mask_v, 0, 1, NORM_MINMAX);
     test.convertTo(proc_img, CV_64F);
     multiply(mask_v, proc_img, proc_img);
     convertScaleAbs(proc_img, proc_img);
     imshow ("Vignette", proc_img);
     waitKey(0);

     return 0;
 }

【讨论】:

    【解决方案4】:

    这是我使用 opencv 对彩色图像进行 Vignette 过滤器的 C++ 实现。它比公认的答案更快。

    #include "opencv2/core/core.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include <iostream>
    
    using namespace cv;
    using namespace std;
    
    double fastCos(double x){
        x += 1.57079632;
        if (x >  3.14159265)
            x -= 6.28318531;
        if (x < 0)
            return 1.27323954 * x + 0.405284735 * x * x;
        else
            return 1.27323954 * x - 0.405284735 * x * x;
    }
    
    double dist(double ax, double ay,double bx, double by){
        return sqrt((ax - bx)*(ax - bx) + (ay - by)*(ay - by));
    }
    
    int main(int argv, char** argc){
        Mat src = cv::imread("filename_of_your_image.jpg");
        Mat dst = Mat::zeros(src.size(), src.type());
        double radius; //value greater than 0, 
                       //greater the value lesser the visible vignette
                       //for a medium vignette use a value in range(0.5-1.5) 
        cin << radius;
        double cx = (double)src.cols/2, cy = (double)src.rows/2;
        double maxDis = radius * dist(0,0,cx,cy);
        double temp;
        for (int y = 0; y < src.rows; y++) {
            for (int x = 0; x < src.cols; x++) {
                temp = fastCos(dist(cx, cy, x, y) / maxDis);
                temp *= temp;
                dst.at<Vec3b>(y, x)[0] =
                        saturate_cast<uchar>((src.at<Vec3b>(y, x)[0]) * temp);
                dst.at<Vec3b>(y, x)[1] =
                        saturate_cast<uchar>((src.at<Vec3b>(y, x)[1]) * temp );
                dst.at<Vec3b>(y, x)[2] =
                        saturate_cast<uchar>((src.at<Vec3b>(y, x)[2]) * temp);
    
            }
        }
        imshow ("Vignetted Image", dst);
        waitKey(0);
    }
    

    【讨论】:

    • 这是一个大胆的声明。介意显示任何基准吗?
    【解决方案5】:

    与 Abid's Answer 类似。但代码是针对彩色图像的

    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    
    img = cv2.imread('turtle.jpg',1)
    rows,cols = img.shape[:2]
    zeros = np.copy(img)
    zeros[:,:,:] = 0
    a = cv2.getGaussianKernel(cols,900)
    b = cv2.getGaussianKernel(rows,900)
    c = b*a.T
    d = c/c.max()
    zeros[:,:,0] = img[:,:,0]*d
    zeros[:,:,1] = img[:,:,1]*d
    zeros[:,:,2] = img[:,:,2]*d
    
    cv2.imwrite('vig2.png',zeros)
    

    原始图片(根据 CC0 许可证取自 Pexels)

    应用 sigma 为 900 的 Vignette 后(即 `cv2.getGaussianKernel(cols,900))

    应用 sigma 为 300 的 Vignette 后(即 `cv2.getGaussianKernel(cols,300))

    此外,您可以通过简单地将高斯的平均值移动到您的焦点,将晕影效果集中到您希望的坐标,如下所示。

    import cv2
    import numpy as np
    
    img = cv2.imread('turtle.jpg',1)
    
    fx,fy = 1465,180 # Add your Focus cordinates here
    fx,fy = 145,1000 # Add your Focus cordinates here
    sigma = 300 # Standard Deviation of the Gaussian
    rows,cols = img.shape[:2]
    fxn = fx - cols//2 # Normalised temperory vars
    fyn = fy - rows//2
    
    zeros = np.copy(img)
    zeros[:,:,:] = 0
    
    a = cv2.getGaussianKernel(2*cols ,sigma)[cols-fx:2*cols-fx]
    b = cv2.getGaussianKernel(2*rows ,sigma)[rows-fy:2*rows-fy]
    c = b*a.T
    d = c/c.max()
    zeros[:,:,0] = img[:,:,0]*d
    zeros[:,:,1] = img[:,:,1]*d
    zeros[:,:,2] = img[:,:,2]*d
    
    zeros = add_alpha(zeros)
    cv2.imwrite('vig4.png',zeros)
    

    海龟图像的尺寸为 1980x1200 (WxH)。下面是一个聚焦在坐标 1465,180(即fx,fy = 1465,180)的例子(注意我已经减少了方差来举例说明焦点的变化)

    以下是关注坐标 145,1000(即fx,fy = 145,1000)的示例

    【讨论】:

    • 我喜欢这种简单性,如果你能分享生成的图像,我会给你+1。
    • @karlphillip 做了一些补充。小插图的焦点可以手动固定。通过一个简单(且明显)的高斯变换技巧
    猜你喜欢
    • 1970-01-01
    • 2020-07-17
    • 1970-01-01
    • 1970-01-01
    • 2012-03-04
    • 2020-05-31
    • 2013-11-10
    • 2011-03-08
    • 1970-01-01
    相关资源
    最近更新 更多