图像处理之分块加速运算

1. 介绍

图像处理的算法复杂度通常都比较高,计算也相应比较耗时。利用CPU多线程处理能力可以大幅度加快计算速度。但是,为了保证多线程处理的结果和单线程处理的结果完全相同,图像的多线程计算有一些需要特别考虑的地方。

基本思路:为了能让多个线程同时并行处理,那么各自处理的数据不能有交集,这很好理解。那么基本思路是将一副图像分成多个子块,每个子块数据肯定是没有交集的,每个线程对一个子块数据进行处理,完成后将所有子块处理结果合成最终图像。

首先,每个子块的大小当然是必须考虑的问题。通常当应用进行一个较长时间的操作,应该用合适的方式告知用户。既然我们把图像分子块处理,如果单个子块处理时间很短,那么每当有一个子块的数据处理完成,我们就可以立即把它相应的处理结果展示给用户。用户就会看到这个图像各个部分的处理结果不断展示出来,直至整个图像完成。这样某种程度上用这种方式就是在告知用户正在处理进行中,避免为了把整个图像处理完成,用户需要等待太长时间。从这个角度来说,如果子块尺寸取的太大,每个子块计算时间肯定相应地加长,对于快速显示部分处理结果给用户是不利的。但是如果子块太小,子块总数就会增加,肯定会增加线程开销和其他一些开销(分割图像,分配子块数据等等),对于总的计算时间是不利的。这是一个权衡问题,可以根据具体情况确定。

另外,很多图像处理都要考虑像素领域范围的信息,因此对于每个子块的处理不能仅仅使用这个子块的内容。具体地,对于靠近子块边缘的像素,还要把子块外的部分像素信息考虑进来,加入计算,才能保证相应像素的处理结果是正确的。准确来说,如果领域半径为r(对方形或圆形领域来说,其他领域可做相应调整),那么子块处理所需要的所有数据是子块四周向外扩展r像素的范围。

2. 实现代码

#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
#include "pthread.h"
#include <time.h>
 
using namespace cv;
using namespace std;
 
#ifndef uchar
#define uchar  unsigned char
#endif
 
#ifndef MAX 
#define MAX(x, y)  (((x) >= (y)) ? (x) : (y))
#endif
 
#ifndef MIN 
#define MIN(x, y)  (((x) >= (y)) ? (y) : (x))
#endif
 
#define CLAMP_XY(x, y) (((x) < 0) ? 0 : ((x) > (y) ? (y) : (x)))
 
#define AXJ_BLUE 	0
#define AXJ_GREEN	1
#define AXJ_RED 	2
#define BMPFORMAT_RGB32_R8G8B8A8	4
 
typedef struct __tag_bmpinfo
{
    
    
	unsigned int dwPixelFormat;
	int lWidth;
	int lHeight;
	int lPitch[3];
	unsigned char* pPlane[3];
}BMPINFO, *LPBMPINFO;
 
typedef struct __filter_info
{
    
    
	BMPINFO *pSrcBitmap;
	float intensity;
 
	int param0;
	int param1;
} FilterInfo, *PFilterInfo;
 
// 转黑白图像
void ConvertToBlackWhite(BMPINFO *pSrcBitmap)
{
    
    
	uchar lightness = 0, max_val = 0, min_val = 0;
	uchar * pSrcData = pSrcBitmap-> pPlane[0];
	int size = pSrcBitmap->lWidth * pSrcBitmap->lHeight;
	for (int i = 0; i < size; i++, pSrcData += 4)
	{
    
    
		max_val = MAX(MAX(pSrcData[AXJ_BLUE], pSrcData[AXJ_GREEN]), pSrcData[AXJ_RED]);
		min_val = MIN(MIN(pSrcData[AXJ_BLUE], pSrcData[AXJ_GREEN]), pSrcData[AXJ_RED]);
		lightness = (max_val + min_val + 1) / 2;
		pSrcData[AXJ_BLUE] = pSrcData[AXJ_GREEN] = pSrcData[AXJ_RED] = lightness;
	}
}
 
// 计算线程
void* ImageFilterThread(void *arg)
{
    
    
	FilterInfo *filter_info = (FilterInfo *)arg;
	BMPINFO *pSrcBitmap = filter_info->pSrcBitmap;
	int intensity = (int)CLAMP_XY(filter_info->intensity * 256, 256);
 
	uchar *dataCopy = (uchar *)malloc(pSrcBitmap->lPitch[0] * pSrcBitmap->lHeight);
	memcpy(dataCopy, pSrcBitmap->pPlane[0], pSrcBitmap->lPitch[0] * pSrcBitmap->lHeight);
 
	// 以彩色图像转黑白图像为例(印象里ps3就是这个算法)
	ConvertToBlackWhite(pSrcBitmap);
 
	// 输出结果
	uchar *src_data = dataCopy;
	uchar *dst_data = pSrcBitmap->pPlane[0];
	int size = pSrcBitmap->lWidth * pSrcBitmap->lHeight;
	for (int i = 0; i < size; i++, src_data += 4, dst_data += 4)
	{
    
    
		dst_data[0] = (src_data[0] * (256 - intensity) + dst_data[0] * intensity) >> 8;
		dst_data[1] = (src_data[1] * (256 - intensity) + dst_data[1] * intensity) >> 8;
		dst_data[2] = (src_data[2] * (256 - intensity) + dst_data[2] * intensity) >> 8;
	}
 
	free(dataCopy);
	dataCopy = NULL;
 
	return NULL;
}
 
// 分块计算
void ImageFilterCommon(BMPINFO *pSrcBitmap, int block_count)
{
    
    
	// 计算分块参数
	int block_src_height = pSrcBitmap->lHeight / block_count;
	int block_src_size = pSrcBitmap->lPitch[0] * block_src_height;
 
	pthread_t *block_thread = (pthread_t *)malloc(block_count * sizeof(pthread_t));
	BMPINFO *block_src_bmp  = (BMPINFO *)malloc(block_count * sizeof(BMPINFO));
	FilterInfo *filter_info_array = (FilterInfo *)malloc(block_count * sizeof(FilterInfo));
 
	// 前n-1块
	int i = 0;
	for (i = 0; i < block_count - 1; i++)
	{
    
    
		memset(&block_src_bmp[i], 0, sizeof(BMPINFO));
		memset(&filter_info_array[i], 0, sizeof(FilterInfo));
 
		block_src_bmp[i].dwPixelFormat = BMPFORMAT_RGB32_R8G8B8A8;
		block_src_bmp[i].lWidth = pSrcBitmap->lWidth;
		block_src_bmp[i].lHeight = block_src_height;
		block_src_bmp[i].lPitch[0] = pSrcBitmap->lPitch[0];
		block_src_bmp[i].pPlane[0] = pSrcBitmap->pPlane[0] + block_src_size * i;
 
		filter_info_array[i].pSrcBitmap = &block_src_bmp[i];
		filter_info_array[i].intensity = 0.8f;
 
		pthread_create(&block_thread[i], NULL, ImageFilterThread, &filter_info_array[i]);
	}
 
	// 最后一块
	i = block_count - 1;
	memset(&block_src_bmp[i], 0, sizeof(BMPINFO));
	memset(&filter_info_array[i], 0, sizeof(FilterInfo));
 
	block_src_bmp[i].dwPixelFormat = BMPFORMAT_RGB32_R8G8B8A8;
	block_src_bmp[i].lWidth = pSrcBitmap->lWidth;
	block_src_bmp[i].lHeight = pSrcBitmap->lHeight - block_src_height * i;
	block_src_bmp[i].lPitch[0] = pSrcBitmap->lPitch[0];
	block_src_bmp[i].pPlane[0] = pSrcBitmap->pPlane[0] + block_src_size * i;
 
	filter_info_array[i].pSrcBitmap = &block_src_bmp[i];
	filter_info_array[i].intensity = 0.8f;
 
	pthread_create(&block_thread[i], NULL, ImageFilterThread, &filter_info_array[i]);
 
	// 阻塞主线程, 等待分块计算完成
	for (i = 0; i < block_count; i++)
	{
    
    
		pthread_join(block_thread[i], NULL);
	}
 
	// todo 结果合成
	// 类似于调色这样的计算, 不需要将各个块合成整幅图像
	// 但如果涉及空间计算, 每个块间需要有重叠部分, 此时需要针对算法特点, 编写合成方法
 
	// 释放资源
	free(block_src_bmp);
	block_src_bmp = NULL;
 
	free(filter_info_array);
	filter_info_array = NULL;
 
	free(block_thread);
	block_thread = NULL;
}
 
int main()
{
    
    
	const char* fileName = "test.png";
	Mat src = imread(fileName);
	imshow("src", src);
 
	BMPINFO srcbmp = {
    
     0 }, texbmp = {
    
     0 }, texbmp2 = {
    
     0 }, lutbmp = {
    
     0 };
	srcbmp.dwPixelFormat = BMPFORMAT_RGB32_R8G8B8A8;
	srcbmp.lWidth = src.cols;
	srcbmp.lHeight = src.rows;
	srcbmp.lPitch[0] = srcbmp.lWidth * 4;
	srcbmp.pPlane[0] = (unsigned char*)malloc(srcbmp.lPitch[0] * srcbmp.lHeight);
 
	uchar *pTempData = src.data;
	uchar *pdata = srcbmp.pPlane[0];
	for (int i = 0; i < src.rows * src.cols; i++, pdata += 4, pTempData += 3)
	{
    
    
		pdata[0] = pTempData[0];
		pdata[1] = pTempData[1];
		pdata[2] = pTempData[2];
		pdata[3] = 255;
	}
 
	///
	clock_t startTime = clock();
	ImageFilterCommon(&srcbmp, 4);
	printf("the time is.... %d ms\n", clock() - startTime);
	///
 
	Mat dst(cv::Size(srcbmp.lWidth, srcbmp.lHeight), src.type());
	pTempData = dst.data;
	pdata = srcbmp.pPlane[0];
	for (int i = 0; i < dst.rows * dst.cols; i++, pdata += 4, pTempData += 3)
	{
    
    
		pTempData[0] = pdata[0];
		pTempData[1] = pdata[1];
		pTempData[2] = pdata[2];
	}
	imshow("dst", dst);
 
	cv::waitKey();
	return 0;
}

3. 最后

猜你喜欢

转载自blog.csdn.net/weixin_45250844/article/details/115150396