拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 使用CUDA和libpng进行影像处理

使用CUDA和libpng进行影像处理

白鹭 - 2022-02-14 2115 0 0

我一直在尝试使用 CUDA 和 libpng 库来处理影像(即应用黑白滤镜)。但是,我不确定如何将影像资料传递给设备。

据我了解,libpng 库读取 png 并使用此函式将信息逐行存盘在名为 row_pointers 的 png_bytep 结构中。

    void read_png_file(char* file_name){
        char header[8];    // 8 is the maximum size that can be checked

        FILE *fp = fopen(file_name, "rb");
        if (!fp)
                abort_("[read_png_file] File %s could not be opened for reading" file_name);
        fread(header, 1, 8, fp);
        // if (png_sig_cmp(header, 0, 8))
        //         abort_("[read_png_file] File %s is not recognized as a PNG file", file_name);


        //Inicializa variables necesarias para libpng
        png_ptr =   png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

        if (!png_ptr)
                abort_("[read_png_file] png_create_read_struct failed");

        info_ptr = png_create_info_struct(png_ptr);
        if (!info_ptr)
                abort_("[read_png_file] png_create_info_struct failed");

        if (setjmp(png_jmpbuf(png_ptr)))
                abort_("[read_png_file] Error during init_io");

        //Inicializa el input/output para el archivo PNG
        png_init_io(png_ptr, fp);
        png_set_sig_bytes(png_ptr, 8);

        //Lee la información anterior a los datos de los píxeles como tal
        png_read_info(png_ptr, info_ptr);

        //Almacena información del archivo PNG 
        width = png_get_image_width(png_ptr, info_ptr);
        height = png_get_image_height(png_ptr, info_ptr);
        color_type = png_get_color_type(png_ptr, info_ptr);
        bit_depth = png_get_bit_depth(png_ptr, info_ptr);

        number_of_passes = png_set_interlace_handling(png_ptr);
        png_read_update_info(png_ptr, info_ptr);


        // Lectura del archivo PNG
        if (setjmp(png_jmpbuf(png_ptr)))
                abort_("[read_png_file] Error during read_image");

        // Reserva el espacio necesario para almacenar los datos del archivo PNG por filas
        row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
        for (y=0; y<height; y  )
                row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));

        // Y para la copia para el device
        d_row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
        for (y=0; y<height; y  )
                d_row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));


        png_read_image(png_ptr, row_pointers);
        
        fclose(fp);
}

我一直在尝试传递存盘在 row_pointers 中的信息,创建一个名为 d_row_pointers 的副本,并使用 cudaMalloc 和 cudaMemcpy 函式,如下所示:

png_bytep * row_pointers;
png_bytep * d_row_pointers;

int main(int argc, char **argv)
{       
        
        // Verifica los parámetros para ejecutar el programa
        if (argc != 3)
                abort_("Uso: ./Nombre_del_Programa <file_in> <file_out>");

        read_png_file(argv[1]);

        // CUDA
        int size = sizeof(png_bytep);
        int int_size = sizeof(int);
        
        cudaMalloc((void **)&d_row_pointers, size);
        for (y=0; y<height; y  )
                 cudaMalloc((void **)&d_row_pointers[y],png_get_rowbytes(png_ptr,info_ptr));

        cudaMalloc((void **)&d_width, int_size);
        cudaMalloc((void **)&d_height, int_size);


        cudaMemcpy(d_row_pointers, &row_pointers, size, cudaMemcpyHostToDevice);
        for (y=0; y<height; y  )
                 cudaMemcpy(d_row_pointers[y], &row_pointers[y], png_get_rowbytes(png_ptr,info_ptr), cudaMemcpyHostToDevice);
        cudaMemcpy(d_width, &width, int_size, cudaMemcpyHostToDevice);
        cudaMemcpy(d_height, &height, int_size, cudaMemcpyHostToDevice);

        // Lanzar el kernel
        process_file<<<1,1>>>(d_row_pointers, d_width, d_height);

        // Copiar los resultados de vuelta al host
        cudaMemcpy(&row_pointers, d_row_pointers, size, cudaMemcpyDeviceToHost);

        // Limpieza
        for (y=0; y<height; y  )
                cudaFree(d_row_pointers[y]);
        cudaFree(d_row_pointers);
        cudaFree(d_width);
        cudaFree(d_height);

        // Escritura de la imagen con los resultados
        write_png_file(argv[2]);

        return 0;
}

这是应该应用过滤器的 process_file 函式

__global__ void process_file(png_bytep * d_row_pointers, int * d_width, int * d_height)
{
        // Se realizan los cambios deseados en la imagen
        
        //Verificar los datos recibidos
        printf("Width = %d , Height = %d ", *d_width, *d_height);

        int rgb_total = 0;  
        float rgb_average = 0.0;
        int x = 0;
        int y = 0;
        png_byte *row;
        png_byte *ptr;

        for (y=0; y<*d_height-1; y  ) {

                for (x=0; x<*d_width; x  ) {
                
                        rgb_total = 0;
                        rgb_average = 0;

                        row             = d_row_pointers[y];
                        ptr             = &(row[x*3]);
                        
                        printf("Pixel  %d - %d, Rgb values: %d - %d - %d \n", x, y, ptr[0], ptr[1], ptr[2]); 
                        rgb_total       = ptr[0]   ptr[1]   ptr[2];
                        
                        // Calculando el promedios RGB
                        rgb_average = rgb_total / 3;
                        // printf("Average: %d \n", (int)rgb_average);
                        
                        ptr[0]  = (int)rgb_average;
                        ptr[1]  = (int)rgb_average;
                        ptr[2]  = (int)rgb_average;
                        
                        // printf("Changed to  %d - %d - %d \n",ptr[0], ptr[1], ptr[2]); 
                        // printf("Pixel  %d - %d done\n",x,y);  
                }

        }
        // printf("Para la imagen de resolución: %d x %d - ", *width, *height);
        
}

但我无法让它作业,即使内核似乎正在接收结构,当我访问资料时它全为零,当它被认为是 RGB 值时。我非常感谢有关将这些资料传递给内核的正确方法的任何帮助。谢谢!

PD:完整的代码可以在这里找到

uj5u.com热心网友回复:

我很确定这条线会导致问题:

cudaMemcpy(d_row_pointers[y], &row_pointers[y], 
    png_get_rowbytes(png_ptr,info_ptr), cudaMemcpyHostToDevice);

您无法d_row_pointers[y]从 cpu 代码访问资料cudaMalloc 需要一个指向 cpu 上的指标的指标,并将这样处理自变量。

您需要小心 CUDA 中的多维阵列。大多数人选择使用平面阵列,即使是影像。

这就是我的建议:将资料复制到平面(一维)缓冲区,然后将其复制到设备。这样会更难犯错误。

啊,还有一件事:您不需要明确地 cudaMalloc 和 cudaMemcpy 内核的宽度和高度自变量。您可以按值传递它们,就好像它是一个普通函式一样。您只需要那些用于阵列的函式。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *