Creazione di un filtro vignetta in opencv?

Creazione di un filtro vignetta in opencv?

Prima di tutto, Abid Rahman K descrive il modo più semplice per utilizzare questo filtro. Dovresti studiare seriamente la sua risposta con tempo e attenzione. Anche l'interpretazione di Wikipedia sulla vignettatura è piuttosto chiarificatrice per coloro che non avevano mai sentito parlare di questo filtro.

L'implementazione di Browny di questo filtro è considerevolmente più complessa. Tuttavia, ho portato il suo codice sull'API C++ e l'ho semplificato in modo da poter seguire le istruzioni da solo.

#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);
    */

Cosa img assomiglia a:

    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);
    */

Cosa maskImg assomiglia a:

    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;
}

Quale output assomiglia a:

Come indicato nel codice sopra, modificando i valori di firstPt , radius e power puoi ottenere effetti artistici più forti/più deboli.

Buona fortuna!


Puoi eseguire una semplice implementazione utilizzando i kernel gaussiani disponibili in OpenCV.

  1. Carica l'immagine, ottieni il suo numero di righe e colonne
  2. Crea due kernel gaussiani di dimensioni righe e colonne, diciamo A,B. La sua variazione dipende dalle tue esigenze.
  3. C =transpose(A)*B, ovvero moltiplica un vettore colonna con un vettore riga in modo tale che l'array di risultati abbia le stesse dimensioni di quella dell'immagine.
  4. D =C/C.max()
  5. E =img*D

Vedi l'implementazione di seguito (per un'immagine in scala di grigi):

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)

Di seguito il mio risultato:

Allo stesso modo per l'immagine a colori:

NOTA :Ovviamente è centrato. Dovrai apportare ulteriori modifiche per spostare lo stato attivo in altre posizioni.


Simile vicino alla risposta di Abid. Ma il codice è per l'immagine colorata

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)

Immagine originale (presa da Pexels con licenza CC0)

Dopo aver applicato la vignettatura con un sigma di 900 (cioè `cv2.getGaussianKernel(cols,900))

Dopo aver applicato la vignettatura con un sigma di 300 (cioè `cv2.getGaussianKernel(cols,300))

Inoltre puoi focalizzare l'effetto vignettatura sulle coordinate del tuo desiderio semplicemente spostando la media della gaussiana sul tuo punto di messa a fuoco come segue.

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)

La dimensione dell'immagine della tartaruga è 1980x1200 (LxA). Quello che segue è un esempio incentrato sulla coordinata 1465,180 (cioè fx,fy = 1465,180 ) (Nota che ho ridotto la varianza per esemplificare il cambio di messa a fuoco)

Quello che segue è un esempio incentrato sulla coordinata 145,1000 (cioè fx,fy = 145,1000 )