Universidad Nacional de Colombia

POSIX

PThread

Es una librería del paquete POSIX del lenguaje C que permite el diseño e implementación de llamadas a procedimientos, con el objetivo de crear flujos independientes de instrucciones y que el sistema operativo ejecutará 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 pthread.h y OpenCV.hpp

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 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.




El siguiente paso es hallar el coeficiente de carga para los hilos, el cual permitirá asignar de forma equitativa la cantidad de trabajo a cada uno de ellos (blockwise).


Posteriormente iniciamos el control de asignación mediante un mutex del ID del hilo, con el objetivo de que no exista condición de carrera.






Ahora, le decimos al sistema cuantos hilos debe ejecutar, y creamos uno a uno la paralelización en la función execute.





En esta función tenemos varios hechos por resaltar. El primero es el radio que es hallado mediante la conversión del kernel.
El segundo hecho es el mutex que evita que los ID de hilo se me repita o solapen.
El tercero es el cálculo del píxel de inicio y el píxel final, que me permite a inclusión total del número de píxeles en el proceso y una carga balanceada de diferencia máxima entre ellos de 1 píxel.
El cuarto es el recorrido en forma de arreglo por cada uno de los píxeles, y el llamado a una función de que devolverá el promedio de los k píxeles vecinos al píxel p.
El quinto y último es la asignación directa a la imagen del resultado obtenido a la imagen.









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 <pthread.h>
#include <opencv2/opencv.hpp>
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];

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
}
double npft = (double)rows * (double)cols  / (double)nThreads; // # of pixels for thread
if( pthread_mutex_init( &mutex, NULL ) != 0){
    cout << "mutex init failed" << endl;
    return 1;
}
pthread_t thread[nThreads];
for( int i = 0; i < nThreads; i++){
    if( pthread_create( &thread[i], NULL, execute, NULL) != 0 ){
        cout << "error pthread_create" <<endl;
        return -1;
    }
}
void *execute( void * z ){
    int r = (kernel - 1) / 2; // radius
    int pthread_mutex_lock(pthread_mutex_t *mutex);
    int n = IDThread;
    IDThread++;
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    int b = (int) (npft * n); // start pixel
    int e = (int) (npft * (n + 1)) - 1; // end pixel
    for( int p = b; p <= e; p++ ){
        int* BGR = average( (int)p/cols, p%cols, r );
        image.at<Vec3b>( (int)p/cols, p%cols )[2] = BGR[2];
        image.at<Vec3b>( (int)p/cols, p%cols )[1] = BGR[1];
        image.at<Vec3b>( (int)p/cols, p%cols )[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;
}
for( int i = 0; i < nThreads; i++ ){
    if( pthread_join( thread[i], NULL ) < 0 ){
        cout << "error pthread_join" <<endl;
        return -1;
    }
}
pthread_mutex_destroy(&mutex);
delete [] img;
namedWindow("modified image", WINDOW_NORMAL );
imshow("modified image", image);
waitKey(0);