Introduction

Digital Image Processing

This page contains codes and exercises that were developed for the Digital Image Processing course (2018.2) at the Universidade Federal do Rio Grande do Norte (UFRN - Brazil).

Unit 1

In this unit, the following examples were developed:

  • Negative of a region
  • Swapping regions
  • Counting bubbles with holes
  • Histogram equalization
  • Motion detection
  • Laplacian of Gaussian
  • Tilty Shift

Exercise 1 - Negative of a region

This is the first exercise using OpenCV image manipulation. In this example, we'll be reading and processing a region of a given image in order to create a negative effect on it. To achive this goal, the following fomula is used to invert the color of the pixels: pixel_color = 255 - pixel_color.

As the image used is in grayscale, we will be aplying it for one channel only. The region is seleted by a couple of for-loops that go through an area within the image, inverting its pixels, as shown below.

Negative Region Negative Region

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{
  Mat image;
  int pxl;

  image= imread("img/camera__.jpg",CV_LOAD_IMAGE_GRAYSCALE);
  if(!image.data)
    cout << "couldn't load image" << endl;

  namedWindow("window",WINDOW_AUTOSIZE);

  for(int i=100;i<200;i++){
    for(int j=100;j<200;j++){
      pxl = image.at<uchar>(i,j);
      image.at<uchar>(i,j) = 255 - pxl;
    }
  }
  imwrite("negative.jpg", image);
  imshow("window", image); 

  waitKey();

  return 0;
}   

Exercise 2 - Swapping regions

In this example, we'll create an image by swapping regions from an original one. For this, Regions of Interest (ROI) were created using rectangles with half the height and width of the original image. In sequence, using the Mat (const Mat &m, const Rect &roi) constructor, 4 images were created by using those rectangles and finally the CV::Mat::copyTo(OutPutArray m) method was used to perfom the swap of the 1-3 and 2-4 regions.

Swapping Regions Swapping Regions

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{
  Mat image = imread("img/camera__.jpg",CV_LOAD_IMAGE_GRAYSCALE);

  if(!image.data)
    cout << "nao abriu camera__.jpg" << endl;

  namedWindow("janela",WINDOW_AUTOSIZE);

  imshow("janela", image);      
  waitKey();

  int x = 0, y = 0, width = image.rows/2, height = image.cols/2;
  Mat image2; 
  image.copyTo(image2);

 // creating Regions of Interest
  Rect rois[4] = { 
        Rect(     0,      0, width, height),
        Rect(width,      0, width, height),
        Rect(     0, height, width, height),
        Rect(width, height, width, height)
        };

  // copying regions and swapping them   
  Mat region1 (image, rois[3]);
  region1.copyTo(image2(rois[0]));

  Mat region2 (image, rois[2]);
  region2.copyTo(image2(rois[1]));

  Mat region3 (image, rois[1]);
  region3.copyTo(image2(rois[2]));

  Mat region4 (image, rois[0]);
  region4.copyTo(image2(rois[3]));

  imwrite("swapping.jpg", image2);
  imshow("janela", image2);  
  waitKey();

  return 0;
}   

Exercise 3. Labeling objects

Example: Bubbles with holes

In this example, we'll introduce the labeling concept, a method which allow us to identify objects or regions in an image. To create labels, we made use of the floodfill OpenCV method, which consists of painting an area that has the same color, replacing it by another color (label). Here this technique is used for labeling and counting white bubbles in a black background,identifing and counting as well, bubbles with holes.

This example was based on the labeling.cpp algorithm and answers to the proposed exercises at agostinhobritojr.github.io.

The labeling.cpp algorithm works very well in cases where the amount of objects are below 255 units. But, if we have more than 255 objects to label, varying from the pixel grayscale intesity, we'll find an issue. For theses cases, another approach could be used, for instance, a diferent number representation as such as floating point can be used, so a much greater amount of objects could be labeled.

labeling labeling labeling labeling

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;

int main(){
  Mat image, mask;
  int width, height;
  int nobjects, holes, counter;

  // - Reading image --  
  CvPoint p;
  image = imread("img/bolhas.png",CV_LOAD_IMAGE_GRAYSCALE);
  
  if(!image.data){
    std::cout << "Couldn't load the file correctly\n";
    return(-1);
  }

  width=image.size().width;
  height=image.size().height;

  p.x = 0;
  p.y = 0;

  //  -- Excluding objects that touch the first and last lines --
  for(int i=0; i<height; i++){
    if(image.at<uchar>(0, i) == 255){ 
        floodFill(image,Point(i, 0), 0); 
    }
    if(image.at<uchar>(width-1, i) == 255){ 
        floodFill(image,Point(i, width-1), 0);
    }
  } 

  //  -- Excluding objects that touch the first and last columns --
  for(int i=0; i<width; i++){
    if(image.at<uchar>(i,0) == 255){ 
        floodFill(image,Point(0, i), 0);
    }
    if(image.at<uchar>(i,height-1) == 255){ 
        floodFill(image,Point(height-1, i), 0);
    }
  }

  //  -- Looking for objects and labeling them --
  nobjects=0; 
  for(int i=0; i<height; i++){
    for(int j=0; j<width; j++){
      if(image.at<uchar>(i,j) == 255){ 
        // Found an white object 
        nobjects++;
        p.x=j;
        p.y=i;
        floodFill(image,p,nobjects);
      }
    }
  }

 //  -- Changing background Color --
  floodFill(image,Point(0,0),255);

 //  -- Looking for bubbles with holes --
  holes = 0; counter = 1;
  for(int i=0; i<height; i++){
    for(int j=0; j<width; j++){
      if(image.at<uchar>(i,j) == 0 && (int)image.at<uchar>(i,j-1)>counter){ //se encontrar um buraco e já não tiver contado a bolha
        // Found a bubble with a hole 
        counter++;
        p.x=j-1;
        p.y=i;
        floodFill(image,p,counter);
      }
    }
  }

  std:: cout << "Number of bubbles: " <<  nobjects << " and number of bubbles with holes: " << counter <<std::endl;

  imwrite("labeling.png", image);
  imshow("janela", image); 
  waitKey();
  return 0;
} 

As a result of the image processing, the output is: labeling

Exercise 4. Equalizing an image

An image histogram is a graphical representation of the intensity distribution of an image. It quantifies the number of pixels for each intensity value considered. OpenCV - Histogram equalization Histogram equalization is a method that improves the contrast in an image, in order to stretch out the intensity range. In this example, in equalize.cpp this outcome has been achived by using OpenCV function:equalize_hist:equalizeHist <>.

original equalized

This example was based on the histogram.cpp algorithm and answers to the proposed exercises at agostinhobritojr.github.io.

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

/**  @function main */
int main( int argc, char** argv )
{
  Mat src, dst;

  string source_window = "Source image";
  string equalized_window = "Equalized Image";

  /// Camera 
  VideoCapture cap;

  //start camera
  cap.open(0);

  if(!cap.isOpened()){
    cout << "Camera not available";
    return -1;
  }

  while(1){
    /// capture image
    cap >> src;

    /// Convert to grayscale
    cvtColor( src, src, CV_BGR2GRAY );

    /// Apply Histogram Equalization
    equalizeHist( src, dst );

    /// Display results
    namedWindow( source_window, CV_WINDOW_NORMAL );
    namedWindow( equalized_window, CV_WINDOW_NORMAL );

    imshow( source_window, src );
    imshow( equalized_window, dst );

    if(waitKey(30) >= 0) break;
    }

  // Closes all the windows
  destroyAllWindows();

  return 0;
}

What does this program do?

Create a VideoCapture object
Capture frame-by-frame
Convert the original image to grayscale
Equalize the Histogram by using the OpenCV function EqualizeHist
Display the source and equalized images in a window.

As we can see from the output image, the equalized image has a better distribution of the overall contrast, the furniture behind can be easily observed now. Original image is on the rigth and equalized on the left side equalize

Exercise 5. Motion Detection

Analizing an image histogram, it is possible to observe several aspects of an image or a video, such as motion detection. In this example, we use the correlation between two frames of an video to determine if there was motion or not in it.

To achive this goal, the OpenCV CalcHist function was used to get the histogram of the frame, and the comparison between two frames was perfomed by the compareHist function, through the correlation comparison.;

What does this program do?

Create a VideoCapture object
Capture frame-by-frame
Calculate histogram by using the OpenCV function 
Display the source and equalized images in a window.

Calculates a histogram of a set of arrays: C++: void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false ) OpenCV implements the function compareHist to perform a comparison. It also offers 4 different metrics to compute the matching, in this program we used the correlation as an metric. compareHist(histR, old_hist, CV_COMP_CORREL);

This example was based on the histogram.cpp algorithm and answers to the proposed exercises at agostinhobritojr.github.io.

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv){
  Mat image;
  int width, height;
  VideoCapture cap;
  vector<Mat> planes;
  Mat histR, motion, old_hist;
  int nbins = 64;
  float range[] = {0, 256};
  const float *histrange = { range };
  bool uniform = true;
  bool acummulate = false;

  // Start Camera
  cap.open(0);
  
  if(!cap.isOpened()){
    cout << "cameras indisponiveis";
    return -1;
  }
  
  // image size
  width  = cap.get(CV_CAP_PROP_FRAME_WIDTH);
  height = cap.get(CV_CAP_PROP_FRAME_HEIGHT);

  cout << "largura = " << width << endl;
  cout << "altura  = " << height << endl;

  // mat for histogram - analyzing only red here.
  int histw = nbins, histh = nbins/2;
  Mat histImgR(histh, histw, CV_8UC3, Scalar(0,0,0));

  // Motion alert
  motion = imread("img/motion_detected.png",CV_LOAD_IMAGE_COLOR);
  if(!motion.data)
    cout << "couldn't load image" << endl;

  namedWindow("window",WINDOW_AUTOSIZE);

  // faz uma vez para poder, calcular a primeira correlação, dar continuidade dentro do loop
  cap >> image;
  split (image, planes);
  calcHist(&planes[0], 1, 0, Mat(), histR, 1,
             &nbins, &histrange,
             uniform, acummulate);
  normalize(histR, histR, 0, histImgR.rows, NORM_MINMAX, -1, Mat());
  
  histR.copyTo(old_hist);

  while(1){
    histR.copyTo(old_hist);
    cap >> image;
    split (image, planes);
    calcHist(&planes[0], 1, 0, Mat(), histR, 1,
             &nbins, &histrange,
             uniform, acummulate);

    normalize(histR, histR, 0, histImgR.rows, NORM_MINMAX, -1, Mat());

    histImgR.setTo(Scalar(0));
    
    double compare = compareHist(histR, old_hist, CV_COMP_CORREL);
    cout << compare << endl;

    if ( compare < 0.01)
    {
      cout << "Motion detected\n";
      motion.copyTo(image(Rect(0, histh, motion.size().width, motion.size().height)));
    }

    for(int i=0; i<nbins; i++){
      line(histImgR,
           Point(i, histh),
           Point(i, histh-cvRound(histR.at<float>(i))),
           Scalar(0, 0, 255), 1, 8, 0);
    }
    histImgR.copyTo(image(Rect(0, 0       ,nbins, histh)));
    imshow("image", image);
    if(waitKey(30) >= 0) break;
    old_hist.copyTo(histR);

  }
  return 0;
}

Exercise 6. Laplacian of a Gaussian filter

This example was based on the filtroespacial.cpp algorithm and answers to the proposed exercises at agostinhobritojr.github.io.

This program adds one more function to the provided example. Through this new function, the laplacian of the gaussian of a captured image can be calculated by pressing the key "z" and the filtered image is displayed.

Comparing both laplacian and laplacion of gauss filters results, it is possible to conclude that the second filter returns a better filtering, as we can observe more easily the borders and cornes of the image.

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void printmask(Mat &m){
  for(int i=0; i<m.size().height; i++){
    for(int j=0; j<m.size().width; j++){
      cout << m.at<float>(i,j) << ",";
    }
    cout << endl;
  }
}

void menu(){
  cout << "\npressione a tecla para ativar o filtro: \n"
    "a - calcular modulo\n"
    "m - media\n"
    "g - gauss\n"
    "v - vertical\n"
    "h - horizontal\n"
    "l - laplaciano\n"
    "z - laplaciano do gaussiano\n"
    "esc - sair\n";
}

int main(int argvc, char** argv){
  VideoCapture video;
  float media[] = {1,1,1,
                   1,1,1,
                   1,1,1};
  float gauss[] = {1,2,1,
                   2,4,2,
                   1,2,1};
  float horizontal[]={-1,0,1,
                      -2,0,2,
                      -1,0,1};
  float vertical[]={-1,-2,-1,
                    0,0,0,
                    1,2,1};
  float laplacian[]={0,-1,0,
                     -1,4,-1,
                     0,-1,0};
  float lapgauss[]={0,0,1,0,0,
                    0,1,2,1,0,
                    1,2,-16,2,1,
                    0,1,2,1,0,
                    0,0,1,0,0};


  Mat cap, frame, frame32f, frameFiltered;
  Mat mask(3,3,CV_32F), mask1;
  Mat result, result1;
  double width, height, min, max;
  int absolut;
  char key;

  video.open(0);
  if(!video.isOpened())
    return -1;
  width=video.get(CV_CAP_PROP_FRAME_WIDTH);
  height=video.get(CV_CAP_PROP_FRAME_HEIGHT);
  std::cout << "largura=" << width << "\n";;
  std::cout << "altura =" << height<< "\n";;

  namedWindow("filtroespacial",1);

  mask = Mat(3, 3, CV_32F, media);
  scaleAdd(mask, 1/9.0, Mat::zeros(3,3,CV_32F), mask1);
  swap(mask, mask1);
  absolut=1; // calcs abs of the image

  menu();
  for(;;){
    video >> cap;
    cvtColor(cap, frame, CV_BGR2GRAY);
    flip(frame, frame, 1);
    imshow("original", frame);
    frame.convertTo(frame32f, CV_32F);
    filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);
    if(absolut){
      frameFiltered=abs(frameFiltered);
    }
    frameFiltered.convertTo(result, CV_8U);
    imshow("filtroespacial", result);
    key = (char) waitKey(10);
    if( key == 27 ) break; // esc pressed!
    switch(key){
    case 'a':
      menu();
      absolut=!absolut;
      break;
    case 'm':
      menu();
      mask = Mat(3, 3, CV_32F, media);
      scaleAdd(mask, 1/9.0, Mat::zeros(3,3,CV_32F), mask1);
      mask = mask1;
      printmask(mask);
      break;
    case 'g':
      menu();
      mask = Mat(3, 3, CV_32F, gauss);
      scaleAdd(mask, 1/16.0, Mat::zeros(3,3,CV_32F), mask1);
      mask = mask1;
      printmask(mask);
      break;
    case 'h':
      menu();
      mask = Mat(3, 3, CV_32F, horizontal);
      printmask(mask);
      break;
    case 'v':
      menu();
      mask = Mat(3, 3, CV_32F, vertical);
      printmask(mask);
      break;
    case 'l':
      menu();
      mask = Mat(3, 3, CV_32F, laplacian);
      printmask(mask);
      break;
    case 'z':
      menu();
      mask = Mat(5, 5, CV_32F, lapgauss);
      printmask(mask);
    default:
      break;
    }
  }
  return 0;
}

Exercise 7. Tilty Shift

This example was based on the addweighted.cpp algorithm and answers to the proposed exercises at agostinhobritojr.github.io.

This program adds three more functions to the provided example. Through these functions, the tiltyshift.cpp program makes the following adjustments:

  1. Adjust the height of the focus centre of the image
  2. Adjust the decay of the blurred area
  3. Adjust the vertical position of the focus centre area of the filtered image
#include <iostream>
#include <opencv2/opencv.hpp>
#include <cmath>

using namespace cv;
using namespace std;

double alfa;


int center_slider=0;
int center_slider_max=100;

int alfa_slider = 0;
int alfa_slider_max = 100;
// height
int top_slider = 0;
int top_slider_max = 100;
Mat image1, image2, blended;
Mat imageTop;

char TrackbarName[50];

void on_trackbar_blend(int, void*){
 alfa = (double) alfa_slider/alfa_slider_max ;
 addWeighted( image2, alfa, imageTop, 1-alfa, 0.0, blended);
 imshow("addweighted", blended);
}

void on_trackbar_line(int, void*){
  image2.copyTo(imageTop);

  int width = image2.size().width;
  int height = image2.size().height;
  int limit = top_slider*height/100;
  int base = center_slider*height/100;


  if(limit > 0){


    if(base >= 0 && base <= height-limit){
      Mat tmp = image1(Rect(0, base, width,limit));
      tmp.copyTo(imageTop(Rect(0, base, width,limit)));
    }

    else{
      Mat tmp = image1(Rect(0, 0, width,limit));
      tmp.copyTo(imageTop(Rect(0, 0, width,limit)));
    }
  }

  on_trackbar_blend(alfa_slider,0);
}

int main(int argvc, char** argv){
  image1 = imread("img/sydney7.jpg");
  resize(image1,image1,Size(640,480));
  image1.copyTo(image2);
  namedWindow("addweighted", 1);
  image2.convertTo(image2,CV_32F);

  
  float media[]={1,1,1,
               1,1,1,
               1,1,1};
  Mat mascara;
  mascara = Mat(3,3,CV_32F,media);
  scaleAdd(mascara, 1/9.0, Mat::zeros(3,3,CV_32F), mascara);

  for (int i = 0; i < 7; i++) {
        filter2D(image2, image2, image2.depth(), mascara, Point(1,1), 0);
  }
  image2.convertTo(image2, CV_8U);
  image2.copyTo(imageTop);
  sprintf( TrackbarName, "Decay x %d", alfa_slider_max );
  createTrackbar( TrackbarName, "addweighted",
          &alfa_slider,
          alfa_slider_max,
          on_trackbar_blend );
  on_trackbar_blend(alfa_slider, 0 );

  sprintf( TrackbarName, "Hight x %d", top_slider_max );
  createTrackbar( TrackbarName, "addweighted",
          &top_slider,
          top_slider_max,
          on_trackbar_line );
  on_trackbar_line(top_slider, 0 );

  sprintf( TrackbarName, "Center x %d", top_slider_max );
  createTrackbar( TrackbarName, "addweighted",
          &center_slider,
          center_slider_max,
          on_trackbar_line );
  on_trackbar_line(center_slider, 0 );

  waitKey(0);
  imwrite("tiltyshift.jpg", blended);
  imshow("window",blended);
  imwrite("original.jpg", image1);
  imshow("window2",image1);
  return 0;
}

Original image Original image

Filtered image Filtered image

Unit 2

In this unit, the following examples were developed:

  • Homomorphic filtering
  • Pointillism art
  • The K-means process

Exercise 1. Homomorphic filter

This example implements an homomorphic filter based on the dft.cpp algorithm and answers to the proposed exercises at agostinhobritojr.github.io.

The Homomorphic filter is based on the following function:

Equation

What does this program do?

The program allows 4 different adjustments in the image

  1. Regulate the low frequency component γLγL (referring to illumination);
  2. Regulate the high frequency component γHγH (referring to reflectance);
  3. Regulate the variable C, which controls the slope of the function as it transitions between γLγL and γHγH.
  4. Adjust the variable D0D0;

Original image Original image

Filtered image Filtered image

#include <iostream>
#include <opencv2/opencv.hpp>
#include <cmath>

using namespace cv;
using namespace std;

Mat image, imageDft, padded;
char *filename;
float MAX = 100.0;
//-----------------VARIAVEIS DO FILTRO---------------------
float yl = 0, yh = 0, d0 = 0, c = 0;
float ylmax = 100, yhmax = 100, d0max = 256, cmax = 100;
int yl_slider = 0, yh_slider = 0, d0_slider = 0, c_slider = 0;
//---------------------------------------------------------

//valores ideais dos tamanhos da imagem para calculo da DFT
int dft_M, dft_N;
//---------------------------------------------------------

char TrackbarName[50];

void deslocaDFT(Mat& image ){
    Mat tmp, A, B, C, D;

    // se a imagem tiver tamanho impar, recorta a regiao para
    // evitar cÃģpias de tamanho desigual
    image = image(Rect(0, 0, image.cols & -2, image.rows & -2));
    int cx = image.cols/2;
    int cy = image.rows/2;

    // reorganize quadrants
    // A B   ->  D C
    // C D       B A
    A = image(Rect(0, 0, cx, cy));
    B = image(Rect(cx, 0, cx, cy));
    C = image(Rect(0, cy, cx, cy));
    D = image(Rect(cx, cy, cx, cy));

    // A <-> D
    A.copyTo(tmp);  D.copyTo(A);  tmp.copyTo(D);

    // C <-> B
    C.copyTo(tmp);  B.copyTo(C);  tmp.copyTo(B);
}

void filtroHM(){
  Mat filter = Mat(padded.size(), CV_32FC2, Scalar(0));
  Mat tmp = Mat(dft_M, dft_N, CV_32F);

  for (int i = 0; i < dft_M; i++) {
      for (int j = 0; j < dft_N; j++) {
          float d2 = pow(i - dft_M/2.0, 2) + pow(j - dft_N/2.0, 2);
          float exp = - c*(d2/pow(d0, 2));
          float filtroH = (yh - yl)*(1 - expf(exp) ) + yl;
          tmp.at<float> (i,j) = filtroH;
      }
  }

  Mat comps[] = {tmp, tmp};
  merge(comps, 2, filter);

  Mat dftClone = imageDft.clone();

  mulSpectrums(dftClone,filter,dftClone,0);

  deslocaDFT(dftClone);

  idft(dftClone, dftClone);

  vector<Mat> planos;

  split (dftClone, planos);

  normalize(planos[0], planos[0], 0, 1, CV_MINMAX);

  imshow("Filtro Homomorfico", planos[0]);
  imshow("Original", image);

}

void on_trackbar_yl(int, void*){
    yl = (float) yl_slider;
    yl = ylmax*yl/MAX;
    filtroHM();
}

void on_trackbar_d0(int, void*){
    d0 = d0_slider*d0max/MAX;
    filtroHM();
}

void on_trackbar_yh(int, void*) {
    yh = yh_slider*yhmax/MAX;
    filtroHM();
}
void on_trackbar_c(int, void*) {
    c = c_slider*cmax / MAX;
    filtroHM();
}


int main(int argvc , char** argv){
  //open image
      if (argvc != 2) {
        cerr << "Usage: " << argv[0] << " <img_path>" << endl;
        return 1;
    }

    filename = argv[1];
    image = imread(filename);
  cvtColor(image, image, CV_BGR2GRAY);
  // Identify the optimum size for 
  // Calculating FFT
  dft_M = getOptimalDFTSize(image.rows);
  dft_N = getOptimalDFTSize(image.cols);



  // Image padding
  Mat_<float> zeros;
  copyMakeBorder(image, padded, 0,
                 dft_M - image.rows, 0,
                 dft_N - image.cols,
                 BORDER_CONSTANT, Scalar::all(0));

  // Zero padding - complex part of the matrix
  zeros = Mat_<float>::zeros(padded.size());

  // prepare the complex part 
  imageDft = Mat(padded.size(), CV_32FC2, Scalar(0));

  copyMakeBorder(image, padded, 0,
                 dft_M - image.rows, 0,
                 dft_N - image.cols,
                 BORDER_CONSTANT, Scalar::all(0));

  Mat_<float> realInput = Mat_<float>(padded);

  // insere the two components within the matrices arrays
  vector<Mat> planos;
  planos.push_back(realInput);
  planos.push_back(zeros);

  // combine matrices array into a single
  // complex component
  merge(planos, imageDft);

  // calcula o dft
  dft(imageDft, imageDft);
  deslocaDFT(imageDft);

  namedWindow("Homomorfic filter", WINDOW_NORMAL);

  sprintf( TrackbarName, "Yh");
  createTrackbar( TrackbarName, "Filtro Homomorfico",
                  &yh_slider,
                  MAX,
                  on_trackbar_yh);

  sprintf( TrackbarName, "YL");
  createTrackbar( TrackbarName, "Filtro Homomorfico",
                  &yl_slider,
                  MAX,
                  on_trackbar_yl);

  sprintf( TrackbarName, "D0");
  createTrackbar( TrackbarName, "Filtro Homomorfico",
                  &d0_slider,
                  MAX,
                  on_trackbar_d0 );
  sprintf(TrackbarName, "C");
  createTrackbar( TrackbarName, "Filtro Homomorfico",
                  &c_slider,
                  MAX,
                  on_trackbar_c);
  filtroHM();
  waitKey(0);

  return 0;
}
 

git

Exercise 2. Pointillism art

This section uses Canny Edge Detection, developed by John F. Canny in 1986, to create pointillism arts. To achieve this goal, we can simulate a painting by drawing circles that work out as a pointillism art. The example below perform the desired effect of pointillism through image processing.

The following steps were used to accomplish this effect:

What does this program do?

The program allows 4 different adjustments in the image

  1. Apply canny algorithm on an image.
  2. Use the edges identified by the canny algorithm to draw smaller circles on them;
  3. Uses the Canny threshold to increase the quality of the painting affect.

This example was based on the pointilhismo.cpp and canny.cpp algorithms and it answers to the proposed exercises at [agostinhobritojr.github.io]https://agostinhobritojr.github.io/tutorial/pdi/#_exerc%C3%ADcios_7).

Original image Original image

Canny edge detector Canny-edge detector

Filtered image Filtered image

#include <iostream>
#include "opencv2/opencv.hpp"
#include <fstream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <numeric>
#include <ctime>
#include <cstdlib>

using namespace std;
using namespace cv;


#define STEP 4
#define JITTER 2
#define RAIO 5

int top_slider = 10;
int top_slider_max = 200;

char TrackbarName[50];

Mat image,imgray, border,points;
  int width, height;
  Vec3b colors;
  int x, y;
  vector<int> yrange;
  vector<int> xrange;

void on_trackbar_canny(int, void*){

  Canny(imgray, border, top_slider, 3*top_slider);
  imshow("cannyborders.png", border);
  points = Mat(height, width, CV_8UC3, Scalar(255,255,255));
  std::random_shuffle(xrange.begin(), xrange.end());
  for(auto i : xrange){
    random_shuffle(yrange.begin(), yrange.end());
    for(auto j : yrange){

      if(border.at<uchar>(j,i) == 255)
      {
          x = i+rand()%(2*JITTER)-JITTER+1;
          y = j+rand()%(2*JITTER)-JITTER+1;
          colors = image.at<Vec3b>(y,x);
          circle(points, Point(x,y),2, CV_RGB(colors[2],colors[1],colors[0]), -1, CV_AA);
      }
      else{
          x = i+rand()%(2*JITTER)-JITTER+1;
          y = j+rand()%(2*JITTER)-JITTER+1;
          colors = image.at<Vec3b>(x,y);
          circle(points,
                 cv::Point(y,x),
                 RAIO,
                 CV_RGB(colors[2],colors[1],colors[0]),
                 -1,
                 CV_AA);
      }
    }
  }
  imshow("canny",points);

}

int main(int argc, char**argv){

  image= imread(argv[1],CV_LOAD_IMAGE_COLOR);
  cvtColor(image,imgray, CV_BGR2GRAY);

  srand(time(0));

  if(!image.data){
  cout << "nao abriu" << argv[1] << endl;
    cout << argv[0] << " imagem.jpg";
    exit(0);
  }

  width=image.size().width;
  height=image.size().height;

  xrange.resize(height/STEP);
  yrange.resize(width/STEP);

  iota(xrange.begin(), xrange.end(), 0);
  iota(yrange.begin(), yrange.end(), 0);

  for(uint i=0; i<xrange.size(); i++){
    xrange[i]= xrange[i]*STEP+STEP/2;
  }

  for(uint i=0; i<yrange.size(); i++){
    yrange[i]= yrange[i]*STEP+STEP/2;
  }

  sprintf( TrackbarName, "Threshold_inferior", top_slider_max );

  namedWindow("canny",1);
  createTrackbar( TrackbarName, "canny",
                &top_slider,
                top_slider_max,
                on_trackbar_canny );

  on_trackbar_canny(top_slider, 0 );

  waitKey();
  imwrite("cannypontos.png",points);
  return 0;
}

Exercise 3. K-means proccess

This example was based on the kmeans.cpp algorithm and answers to the proposed exercises at [agostinhobritojr.github.io]https://agostinhobritojr.github.io/tutorial/pdi/#_exerc%C3%ADcios_8).

The following steps were used to accomplish this effect:

What does this program do?

The k-means algorithm works according to the following steps:

  1. Choose k as the number of classes for the vectors xi of N samples, i = 1,2, ⋯, N.
  2. Choose m1, m2, ⋯, mk as initial approximations for the class centers.
  3. Sort each sample xi using, for example, a minimum distance classifier (Euclidean distance).
  4. Recalculate the averages mj using the result of the previous step.
  5. If the new averages are consistent (do not change considerably), finalize the algorithm. If not, recalculate the centers and redo the classification.
#include <opencv2/opencv.hpp>
#include <cstdlib>
#include <ctime>

using namespace cv;

int main( int argc, char** argv ){
  for(int i = 0; i < 10; i++){

    int nClusters = 20;
    Mat labels;
    int nRounds = 1;
    Mat centers;
    char name[30];

    if(argc!=3){
    exit(0);
    }

    Mat img = imread( argv[1], CV_LOAD_IMAGE_COLOR);
    Mat samples(img.rows * img.cols, 3, CV_32F);

    for( int y = 0; y < img.rows; y++ ){
      for( int x = 0; x < img.cols; x++ ){
        for( int z = 0; z < 3; z++){
          samples.at<float>(y + x*img.rows, z) = img.at<Vec3b>(y,x)[z];
      }
    }
    }

    kmeans(samples,
       nClusters,
       labels,
       TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10000, 0.0001),
       nRounds,
       KMEANS_RANDOM_CENTERS,
       centers );


    Mat labeled( img.size(), img.type() );
    for( int y = 0; y < img.rows; y++ ){
      for( int x = 0; x < img.cols; x++ ){
      int indice = labels.at<int>(y + x*img.rows,0);
      labeled.at<Vec3b>(y,x)[0] = (uchar) centers.at<float>(indice, 0);
      labeled.at<Vec3b>(y,x)[1] = (uchar) centers.at<float>(indice, 1);
      labeled.at<Vec3b>(y,x)[2] = (uchar) centers.at<float>(indice, 2);
    }
    }
    sprintf(name,  "test%d.jpg", i);
    imwrite(name, labeled);
    //waitKey( 0 );
  }
}