Solución:
En primer lugar, Abid Rahman K describe la forma más sencilla de utilizar este filtro. Debería estudiar seriamente su respuesta con tiempo y atención. La versión de Wikipedia sobre Vignetting también es bastante esclarecedora para aquellos que nunca habían oído hablar de este filtro.
La implementación de Browny de este filtro es considerablemente más compleja. Sin embargo, porté su código a la API de C ++ y lo simplifiqué para que puedas seguir las instrucciones tú mismo.
#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 imreadn";
return -1;
}
/*
cv::namedWindow("Original", cv::WINDOW_NORMAL);
cv::resizeWindow("Original", img.size().width/2, img.size().height/2);
cv::imshow("Original", img);
*/
Qué img parece:
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);
*/
Qué maskImg parece:
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;
}
Qué producción parece:
Como se indica en el código anterior, al cambiar los valores de firstPt
, radius
y power
puede lograr efectos artísticos más fuertes / más débiles.
¡Buena suerte!
Puede realizar una implementación simple utilizando los núcleos gaussianos disponibles en OpenCV.
- Cargue la imagen, obtenga su número de filas y columnas
- Cree dos núcleos gaussianos de filas y columnas de tamaño, digamos A, B. Su variación depende de sus necesidades.
- C = transponer (A) * B, es decir, multiplicar un vector de columna con un vector de fila de modo que la matriz de resultados tenga el mismo tamaño que el de la imagen.
- D = C / C.max ()
- E = img * D
Vea la implementación a continuación (para una imagen en escala de grises):
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)
A continuación se muestra mi resultado:
Del mismo modo para la imagen en color:
NOTA: Por supuesto, está centrado. Deberá realizar modificaciones adicionales para mover el foco a otros lugares.
Similar a la respuesta de Abid. Pero el código es para la imagen coloreada.
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)
Imagen original (tomada de Pexels con licencia CC0)
Después de aplicar Vignette con un sigma de 900 (es decir, `cv2.getGaussianKernel (cols, 900))
Después de aplicar Vignette con un sigma de 300 (es decir, `cv2.getGaussianKernel (cols, 300))
Además, puede enfocar el efecto de viñeta a las coordenadas de su deseo simplemente cambiando la media del gaussiano a su punto de enfoque de la siguiente manera.
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)
El tamaño de la imagen de la tortuga es 1980×1200 (WxH). El siguiente es un ejemplo que se centra en la cordinate 1465,180 (es decir fx,fy = 1465,180
) (Tenga en cuenta que he reducido la varianza para ejemplificar el cambio de enfoque)
El siguiente es un ejemplo que se centra en la cordinate 145,1000 (es decir fx,fy = 145,1000
)