你的浏览器禁用了JavaScript, 请开启后刷新浏览器获得更好的体验!
首页
热门
推荐
精选
登录
|
注册
k均值图像分割的GPU加速
立即下载
用AI写一个
金额:
5
元
支付方式:
友情提醒:源码购买后不支持退换货
立即支付
我要免费下载
发布时间:2019-06-05
9人
|
浏览:2198次
|
收藏
|
分享
技术:cuda + openmp
运行环境:linux+cuda8.0及以上
概述
本demo实现了基于K均值图像分割的GPU加速,已经在计算能力5.0和6.1的GPU上测试通过,最大加速比在Tesla P4上可达320倍。
详细
图像分割是指把图像分成各具特性的区域并提取出感兴趣目标的技术和过程,是从图像处理到图像分析的关键步骤。K均值聚类算法是目前最受欢迎和应用最为广泛的聚类分析方法之一。K均值聚类算法用于图像分割具有直观、快速、易于实现的特点。不过当图像很大或者k很大时,采用k均值算法进行图像分割会异常缓慢,所以我们需要对其进行加速。幸运的是,k均值算法最核心的步骤具有很高的并行性,这给我们加速带来了很大遍历。我们既可以通过openmp对其进行加速,也可以利用最新的GPU对其进行加速。在本文中我们着重介绍如何利用GPU加速k均值算法,使其可以更快地进行图像分割。要进行图像分割的原始图片如下: ![](/contentImages/image/20190605/rJyHRVBRYKEVDR5YGmp.png) ## k均值算法 k均值算法是最常用和最简单的聚类算法,原理很简单,但是效果还不错。使用k均值算法进行图像分割通常包含如下步骤: 1. 确定k的个数,可以人为指定也可以通过[算法](https://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set)确定; 2. 选择k个初始的像素点当做最初的类别,可以随机初始化也可以划区域初始化; 3. 将每一个像素点和k个类别像素点进行距离计算,将其归入距离最小的类别中; 4. 根据3中计算的每一个像素点类别重新计算k个类别的像素值; 5. 迭代步骤3和4直到精度足够。 在单次迭代中,步骤3的复杂度为O(nk),其中n为图片大小,k为选择的聚类个数;步骤4的复杂度为O(n+k),所以算法的瓶颈为步骤3。幸运的是,步骤3在计算每个像素点属于哪一类时没有任何依赖性,不同的像素点之间可以并行去做,使其具有极大的加速比。相反,步骤4虽然复杂度低,但是在并行化过程中会遇到极高的写冲突,从而限制了其加速效果。 由于步骤3具有很好的并行性,即使没有GPU我们也可以很方便地通过其他手段进行加速,最容易的就是利用openmp并行化for循环。当计算机的核数很多时,此种方法也可以获得比较明显的收益。由于k均值理解比较容易,我们就不给出串行代码实现了。当k为4时,原始图像分割的效果如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190604170316520.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1dGlhbnp1aWppbg==,size_16,color_FFFFFF,t_70) 可以看出当k等于4时,我们已经能比较清晰的看出原图的轮廓了。 ## k均值算法的GPU加速 用GPU加速k均值算法也就是加速步骤3和4,步骤3的kernel称为gen_category,步骤4的kernel称为gen_new_anchor。因为简单来说步骤3就是分类,步骤4就是求新类别。先看一下步骤3的kernel: ```c __global__ void gen_category(pixel* d_in,pixel* d_anchor,unsigned char* d_category,int len,int k) { extern __shared__ pixel shared_anchor[]; if(threadIdx.x
d) { d_category[idx]=i; min_d=d; } } } } ``` 输入参数d_in表示图片的所有像素,len表示像素的个数,d_anchor表示k个初始聚类像素点,d_category表示执行完毕之后每个像素点的类别。在启动kernel的时候,我们每个block启动1024线程,然后启动足够多的block使其能覆盖所有的像素点,所以在上面的kernel中没有for循环。block内的每个线程执行操作都一样,就是和k个初始聚类点进行距离比较,然后归类。因为k个初始聚类点会被多次访问,为了加速其访问我们将k个聚类点放入共享内存中,由于k不确定所以我们采用动态方式分配共享内存。 相比之下,步骤4的kernel要复杂很多。因为在求新聚类点的操作中,我们面临n个元素要写入k个元素的问题,实际中n远大于k,所以在步骤4中存在非常严重的写冲突,必须要通过原子操作进行串行化,这就大大限制了步骤4的加速能力。kernel如下: ```c __device__ int count=0; __global__ void gen_new_anchor(pixel* d_in,unsigned char* d_category,pixel* d_anchor, unsigned int* sum_R,unsigned int* sum_G,unsigned int* sum_B,int* total_count,int len,int k) { extern __shared__ unsigned int dis_sum[]; const int tid=threadIdx.x; __shared__ bool isLast; if(tid<4*k) { dis_sum[tid]=0.0; } __syncthreads(); int idx=blockDim.x*blockIdx.x+threadIdx.x; if(idx
本实例支付的费用只是购买源码的费用,如有疑问欢迎在文末留言交流,如需作者在线代码指导、定制等,在作者开启付费服务后,可以点击“购买服务”进行实时联系,请知悉,谢谢
感谢
3
手机上随时阅读、收藏该文章 ?请扫下方二维码
相似例子推荐
评论
作者
姚光超
1
例子数量
9
帮助
3
感谢
评分详细
可运行:
4.5
分
代码质量:
4.5
分
文章描述详细:
4.5
分
代码注释:
4.5
分
综合:
4.5
分
作者例子
k均值图像分割的GPU加速