Universidad Nacional de Colombia

OpenMP

Parallel for

Esta funcionalidad pertenece al paquete de OpenMP para lenguajes como C, C++, Python, entre otros, que permite el diseño e implementación de llamadas a procedimientos, con el objetivo de crear flujos independientes comunicados mediante memoria compartida y que el sistema operativo ejecuta de manera simultanea según la capacidad física del procesador.
A continuación veremos la implementación de forma paralela del algoritmo Blur Effect mediante la librería OpenMP

El primer paso es incluir todas las librerías que necesitamos. Si se tiene alguna duda de la instalación de alguna de ellas, diríjase al archivo README.md del repositorio y lea detalladamente cada uno de los pasos para poder ejecutar el código.

En segundo lugar hacemos llamado de los namespaces para poder referirnos a las funciones que nos provee la librería iostream y OpenCV sin necesidad de referenciarlos

En tercer lugar, leemos la imagen con el nombre obtenido por consola y la almacenamos en una variable Mat de la librería OpenCV



En cuarto lugar se reservan las variables necesarias para el procedimiento.



Luego, realizamos el cambio en la varible que define la cantidad de hilos a ejecutar en la llamada de OpenMP.

Posteriormente se realiza el llamado a la función de OpenMP definida para la ejecución de hilos. Esa "sencilla" línea de código permite correr de forma paralela el ciclo for que lo sucede. Es importante destacar que la librería de OpenMP realiza el balanceo de carga automáticamente y con el simple llamado ejecuta un correcto paralelismo.

En septimo lugar realizamos el paso de información de los canales RGB de la imagen a valores enteros en una matriz de tamaño rows x cols, donde rows representa el número de filas y cols el número de columnas de la imagen. Cada una de estas posiciones va a almacenar un vector de 3 posiciones, lo que representa los valores de R, G y B.
La razón por la cual se hace este paso es por tiempo de ejecución, ya que si se utiliza la variable Mat para el procedimiento genera una mayor demora en completar el algoritmo.



Esta función es muy simple. Está encargada de calcular el promedio de los k píxeles más cercanos.







Para comenzar, halla la viabilidad de moverse a las posiciones requeridas por el kernel. Es suficiente con hallar dos de las esquinas diagonales para saber el campo donde se debe calcular el promedio sin rebosar la matriz. Por lo tanto las variables x1 y x2 indican los límites en el eje x, y las variables y1 y y2 los límites en y.






Por último ya conociendo los límites, el algoritmo se moverá en ese espacio y calculará el resultado requerido para los espacios de color R, G y B.









Luego de haber ejecutado todo el algoritmo es momento de unir los hilos mediante un join






El último paso que involucra la librería pthread.h es la destrucción del mutex


Se libera la memoria reservada dinámicamente



Y ya para finalizar, se muestra la imagen resultado del algoritmo

#include <iostream.h>
#include <opencv2/opencv.hpp>
#include "omp.h"
using namespace std;
using namespace cv;
Mat image = imread( argv[1], 1 );
int rows = image.rows;
int cols = image.cols;
int ** img = new int * [rows * cols];
omp_set_num_threads(nThreads);

#pragma omp parallel for

for( int i = 0; i < rows * cols; i++) {
    *(img + i) = new int[3];
    *(*(img + i))     = image.at( int(i / cols), i % cols )[0]; // B
    *(*(img + i) + 1) = image.at( int(i / cols), i % cols )[1]; // G
    *(*(img + i) + 2) = image.at( int(i / cols), i % cols )[2]; // R
}
for( int r = 0; r < rows; r++ ){
    for( int c = 0; c < cols; c++ ){
        int* BGR = average( r, c, int((kernel - 1) / 2 ) );
        image.at<Vec3b>( r, c )[2] = BGR[2];
        image.at<Vec3b>( r, c )[1] = BGR[1];
        image.at<Vec3b>( r, c )[0] = BGR[0];
    }
}
int* average( int row, int column, int kernel){
    int x1, x2;
    int y1, y2;
    if( column - kernel >= 0 )
        x1 = column - kernel;
    else
        x1 = 0;
    if( column + kernel < cols )
        x2 = column + kernel;
    else
        x2 = cols-1;
    if( row - kernel >= 0 )
        y1 = row - kernel;
    else
        y1 = 0;
    if( row + kernel < rows )
        y2 = row + kernel;
    else
        y2 = rows-1;
    int R = 0, G = 0, B = 0;
    for( int i = y1; i <= y2; i++){
        for( int j = x1; j <= x2; j++ ){
            int * temp = *(img + i * cols + j);
            R += *(temp + 2);
            G += *(temp + 1);
            B += *(temp);
        }
    }
    int* ans = new int[3];
    int size = (x2 - x1 + 1)*(y2 - y1 + 1);
    ans[0] = (int)(B / size);
    ans[1] = (int)(G / size);
    ans[2] = (int)(R / size);
    return ans;
}
delete [] img;
namedWindow("modified image", WINDOW_NORMAL );
imshow("modified image", image);
waitKey(0);