图像处理之分块加速运算

news/2024/7/21 7:31:33 标签: 图像处理

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


http://www.niftyadmin.cn/n/1715152.html

相关文章

人像分割之ExtremeC3Net

1. 摘要 人像分割任务作为许多任务的一个中间阶段&#xff0c;对实时性要求极高&#xff0c;并且当前缺乏大规模的人像分割数据集&#xff0c;为此论文提出ExtremeC3Net模型和用于进行数据扩充的简单方法。ExtremeC3Net基于改进的C3模块&#xff0c;能够实现精度较高速度极快的…

BSRGAN | 一种针对真实图像退化的盲图像超分模型

1. 摘要 众所周知&#xff0c;当图像超分的预假设退化模型与真实图像的退化方式不匹配时&#xff0c;模型的性能会出现性能下降&#xff0c;甚至负面效果现象。尽管已有集中退化模型考虑的额外的影响因素(比如模糊核以及程度)&#xff0c;但是它们仍然无法有效覆盖真实图像的多…

NNI神经网络模型压缩教程

1. NNI简介 NNI是微软发布的一款自动机器学习&#xff08;AutoML&#xff09;开源项目&#xff0c;对机器学习生命周期的各个环节做了更加全面的支持&#xff0c;包括特征工程、神经网络架构搜索&#xff08;NAS&#xff09;、超参调优和模型压缩在内的步骤&#xff0c;你都能…

torchvision.transforms使用详解

1. 介绍 PyTorch框架中有一个非常重要且好用的包&#xff1a;torchvision&#xff0c;该包主要由3个子包组成&#xff0c;分别是&#xff1a;torchvision.datasets、torchvision.models、torchvision.transforms。这3个子包的具体介绍可以参考官网&#xff1a;http://pytorch.…

3D LUT图像处理

1. 介绍 在我们开始之前&#xff0c;先对必要的背景知识做一些简单的铺垫。LUT 是 Lookup Table 的缩写&#xff0c;在图像处理方面&#xff0c;LUT 可以用来完成类似滤镜的效果&#xff0c;其原理本质上就是一个映射关系&#xff0c;输入颜色 (r, g, b)&#xff0c;通过 LUT …

人脸识别数据集-Glint360K

1. 介绍 学术界的测评比如IJB-C和megaface&#xff0c;利用该数据集很容易刷到SOTA&#xff0c;大家具体可以看论文&#xff0c;这里展示一下IFRT的结果&#xff0c;IFRT又称国产FRVT, IFRT测试集主要有不同肤色的素人构成&#xff0c;相比起IJB-C和megaface更具有模型的区分度…

人脸识别数据集-WebFace260M

1. 介绍 本文介绍一个大规模的人脸识别数据集&#xff1a;WebFace260M&#xff0c;由 4M identities&#xff08;身份&#xff09;和 260M 人脸组成&#xff0c;为百万级深度人脸清洗和识别提供了很好的资源&#xff0c;如图1和Tab.1所示。 又通过提纯&#xff0c;即所设计的…

人脸关键点数据集整理

1. 什么是关键点检测&#xff1f; 人脸关键点检测是指给定人脸图像&#xff0c;定位出人脸面部的关键点&#xff0c;包括眉毛、眼睛、鼻子、嘴巴、脸部轮廓区域的点&#xff0c;由于受到姿态和遮挡等因素的影响&#xff0c;人脸关键点检测是一个富有挑战性的任务。 人脸关键点…