一种基于DeltaE(CIE 1976)的找色算法Cuda实现
Delta E 是评估色彩准确度的重要测量指标。摄影师、影片编辑和平面设计师等创意专业人士都应重视这项标准,因其是选择专业级显示器的重要考虑因素。
常见的找色算法都是基于颜色RGB上的数值差,这种方法虽然快捷,但是和人眼视觉上的色彩并不相同。这里采用Delta E的评估标准找色更符合人眼的直观感觉。
上文使用CPU计算,采用了一些优化方法但是都不尽如人意,这里使用cuda加速提高这个算法的可用度。
//计算颜色之间的Delta E
//<= 1.0:人眼无法感知差异
//1 - 2:仔细观察可以感知差异
//2 - 10:随意一看便可以感知差异
//11 - 49:色彩的相似程度大于相反程度
//100:色彩完全失真
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#include <cmath>
#include <ctime>
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);
struct Color_BGR
{
int B, G, R;
};
struct Color_Lab
{
float L, a, b;
};
Color_Lab BGR2Lab(Color_BGR x)
{
#define gamma(x) (((x) > 0.04045) ? std::pow(((x)+0.055f) / 1.055f, 2.4f) : ((x) / 12.92));
const float param_13 = 1.0f / 3.0f;
const float param_16116 = 16.0f / 116.0f;
const float Xn = 0.950456f;
const float Yn = 1.0f;
const float Zn = 1.088754f;
float RR = gamma(x.R / 255.0);
float GG = gamma(x.G / 255.0);
float BB = gamma(x.B / 255.0);
float X, Y, Z, fX, fY, fZ;
X = 0.4124564f * RR + 0.3575761f * GG + 0.1804375f * BB;
Y = 0.2126729f * RR + 0.7151522f * GG + 0.0721750f * BB;
Z = 0.0193339f * RR + 0.1191920f * GG + 0.9503041f * BB;
X /= (Xn);
Y /= (Yn);
Z /= (Zn);
if (Y > 0.008856f)
fY = std::pow(Y, param_13);
else
fY = 7.787f * Y + param_16116;
if (X > 0.008856f)
fX = std::pow(X, param_13);
else
fX = 7.787f * X + param_16116;
if (Z > 0.008856)
fZ = std::pow(Z, param_13);
else
fZ = 7.787f * Z + param_16116;
float L, a, b;
L = 116.0f * fY - 16.0f;
L = L > 0.0f ? L : 0.0f;
a = 500.0f * (fX - fY);
b = 200.0f * (fY - fZ);
return { L,a,b };
}
cudaError_t FindColorCuda(Color_BGR *src, float *ret,Color_Lab target,unsigned int size);
__global__ void FindColorCudaKernel(Color_BGR *src, float* ret, Color_Lab target)
{
int i = blockIdx.x * 256 + threadIdx.x;
#define gamma(x) (((x) > 0.04045) ? pow(((x)+0.055f) / 1.055f, 2.4f) : ((x) / 12.92));
const float param_13 = 1.0f / 3.0f;
const float param_16116 = 16.0f / 116.0f;
const float Xn = 0.950456f;
const float Yn = 1.0f;
const float Zn = 1.088754f;
float RR = gamma(src[i].R / 255.0);
float GG = gamma(src[i].G / 255.0);
float BB = gamma(src[i].B / 255.0);
float X, Y, Z, fX, fY, fZ;
X = 0.4124564f * RR + 0.3575761f * GG + 0.1804375f * BB;
Y = 0.2126729f * RR + 0.7151522f * GG + 0.0721750f * BB;
Z = 0.0193339f * RR + 0.1191920f * GG + 0.9503041f * BB;
X /= (Xn);
Y /= (Yn);
Z /= (Zn);
if (Y > 0.008856f)
fY = pow(Y, param_13);
else
fY = 7.787f * Y + param_16116;
if (X > 0.008856f)
fX = pow(X, param_13);
else
fX = 7.787f * X + param_16116;
if (Z > 0.008856)
fZ = pow(Z, param_13);
else
fZ = 7.787f * Z + param_16116;
float L, a, b;
L = 116.0f * fY - 16.0f;
L = L > 0.0f ? L : 0.0f;
a = 500.0f * (fX - fY);
b = 200.0f * (fY - fZ);
ret[i] = sqrt((L - target.L) * (L - target.L) + (a - target.a) * (a - target.a) + (b - target.b) * (b - target.b));
}
Color_BGR src_mat[1024 * 1024];
float ret_mat[1024 * 1024];
int main()
{
for (int i = 0; i < 1024 * 1024; i++)
{
src_mat[i] = { std::rand() % 256,std::rand() % 256, std::rand() % 256 };
}
//Pre Run for Best Speed
cudaError_t cudaStatus = FindColorCuda(src_mat, ret_mat, BGR2Lab({ 190,35,41 }), 1024 * 1024);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "FindColorCuda failed!");
return 1;
}
int st = clock();
// Add vectors in parallel.
cudaStatus = FindColorCuda(src_mat, ret_mat, BGR2Lab({190,35,41}), 1024 * 1024);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "FindColorCuda failed!");
return 1;
}
printf("Cost: %d\n", clock() - st);
int count = 0;
for (int i = 0; i < 1024*1024 ; i++)
{
if (ret_mat[i] < 2)
count++;
}
printf("%d", count);
// cudaDeviceReset must be called before exiting in order for profiling and
// tracing tools such as Nsight and Visual Profiler to show complete traces.
cudaStatus = cudaDeviceReset();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaDeviceReset failed!");
return 1;
}
return 0;
}
//Helper
cudaError_t FindColorCuda(Color_BGR* src, float* ret, Color_Lab target, unsigned int size)
{
Color_BGR* dev_src = nullptr;
float* dev_ret = nullptr;
cudaError cudaStatus;
// Choose which GPU to run on, change this on a multi-GPU system.
cudaStatus = cudaSetDevice(0);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaSetDevice failed! Do you have a CUDA-capable GPU installed?");
goto Error;
}
// Allocate GPU buffers for three vectors (two input, one output) .
cudaStatus = cudaMalloc((void**)&dev_src, size * sizeof(Color_BGR));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!");
goto Error;
}
cudaStatus = cudaMalloc((void**)&dev_ret, size * sizeof(float));
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMalloc failed!");
goto Error;
}
// Copy input vectors from host memory to GPU buffers.
cudaStatus = cudaMemcpy(dev_src, src, size * sizeof(Color_BGR), cudaMemcpyHostToDevice);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
FindColorCudaKernel <<<size/256, 256 >>> (dev_src,dev_ret,target);
// Check for any errors launching the kernel
cudaStatus = cudaGetLastError();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "FindColorCuda launch failed: %s\n", cudaGetErrorString(cudaStatus));
goto Error;
}
// cudaDeviceSynchronize waits for the kernel to finish, and returns
// any errors encountered during the launch.
cudaStatus = cudaDeviceSynchronize();
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
goto Error;
}
cudaStatus = cudaMemcpy( ret, dev_ret, size * sizeof(float), cudaMemcpyDeviceToHost);
if (cudaStatus != cudaSuccess) {
fprintf(stderr, "cudaMemcpy failed!");
goto Error;
}
Error:
cudaFree(dev_ret);
cudaFree(dev_src);
return cudaStatus;
}
在4060 LapTop 上取得 8ms(1024*1024)的成绩
一种基于DeltaE(CIE 1976)的找色算法Cuda实现的更多相关文章
- 五种基于RGB色彩空间统计的皮肤检测算法
最近一直在研究多脸谱识别以及如何分辨多个皮肤区域是否是人脸的问题 网上找了很多资料,看了很多篇文章,将其中基于RGB色彩空间识别皮肤 的统计算法做了一下总结,统计识别方法主要是简单相比与很多其它基于 ...
- 一种基于LQR使输出更加稳定的算法(超级实用)
已知: 令: 则: 以上三式成立 具体步骤: 状态量最后一行加入“上一时刻的控制量”: A,B根据上述方法变形: Q,R增加维度(控制量一般都为一个,此时R维度不变): 最关键的是——输出量已经变为“ ...
- (转载)找圆算法((HoughCircles)总结与优化
Opencv内部提供了一个基于Hough变换理论的找圆算法,HoughCircle与一般的拟合圆算法比起来,各有优势:优势:HoughCircle对噪声点不怎么敏感,并且可以在同一个图中找出多个圆 ...
- 转载-找圆算法((HoughCircles)总结与优化-霍夫变换
原文链接: http://www.opencv.org.cn/forum.php?mod=viewthread&tid=34096 找圆算法((HoughCircles)总结与优化 Ope ...
- 找圆算法((HoughCircles)总结与优化
http://www.opencv.org.cn/forum.php?mod=viewthread&tid=34096 Opencv内部提供了一个基于Hough变换理论的找圆算法,Hough ...
- [信安Presentation]一种基于GPU并行计算的MD5密码解密方法
-------------------paper--------------------- 一种基于GPU并行计算的MD5密码解密方法 0.abstract1.md5算法概述2.md5安全性分析3.基 ...
- 一种基于 Numpy 的 TF-IDF 实现报告
一种基于 Numpy 的 TF-IDF 实现报告 摘要 本文使用了一种 state-of-the-art 的矩阵表示方法来计算每个词在每篇文章上的 TF-IDF 权重(特征).本文还将介绍基于 TF- ...
- 26种基于PHP的开源博客系统
26种基于PHP的开源博客系统 来源:本站原创 PHP学习笔记 以下列举的PHP开源Blog系统中,除了我们熟知的WordPress之外,大多都没有使用过,其中一些已经被淘汰,或者有人还在使用.除了做 ...
- Hive数据分析——Spark是一种基于rdd(弹性数据集)的内存分布式并行处理框架,比于Hadoop将大量的中间结果写入HDFS,Spark避免了中间结果的持久化
转自:http://blog.csdn.net/wh_springer/article/details/51842496 近十年来,随着Hadoop生态系统的不断完善,Hadoop早已成为大数据事实上 ...
- LM-MLC 一种基于完型填空的多标签分类算法
LM-MLC 一种基于完型填空的多标签分类算法 1 前言 本文主要介绍本人在全球人工智能技术创新大赛[赛道一]设计的一种基于完型填空(模板)的多标签分类算法:LM-MLC,该算法拟合能力很强能感知标签 ...
随机推荐
- djang中orm使用iterator()
当查询结果有很多对象时,QuerySet的缓存行为会导致使用大量内存.如果你需要对查询结果进行好几次循环,这种缓存是有意义的,但是对于 queryset 只循环一次的情况,缓存就没什么意义了.在这种情 ...
- 【LeetCode字符串#extra】KMP巩固练习:旋转字符串、字符串轮转
旋转字符串 https://leetcode.cn/problems/rotate-string/ 给定两个字符串, s 和 goal.如果在若干次旋转操作之后,s 能变成 goal ,那么返回 tr ...
- 可视化学习:使用WebGL实现网格背景
前言 作为前端开发人员,我们最关注的就是应用的交互体验,而元素背景是最基础的交互体验之一.一般而言,能够使用代码实现的界面,我们都会尽可能减少图片的使用,这主要是有几方面的原因,第一,是图片会消耗更多 ...
- UI自动化测试中图片验证码的处理?
首先定位验证码image的元素,然后通过element.screenshot('a.png')截图保存到本地,通过第三方接口识别本地图片验证码
- 私有网盘服务 dzzoffice部署
官网地址: https://dzzoffice.com/ dzzoffice有多种部署方式, docker方式,源码方式, 经过测试,这两种部署方式在数据进行备份恢复时都会存在一些bug, 所有这里采 ...
- element_ui 知识点整理
第一章复习,树型组件数据填充:数据组件需要的数据绑定到:data 但是具体那些字绑生成标签需要在定义一人:props ="这儿绑定对象" 对象中label children ...
- 将Abp移植进.NET MAUI项目(三):构建UI层
很开心,终于到了创建页面的时候了! 我们需要两个页面 MainPage 主页面 MusicItemPage 条目编辑页面 编写主页面 新建一个MainPageViewModel.cs,作为Main ...
- STM32 LwIP学习过程问题总结(一):LwIP ping不通,抓包发现ICMP校验和为0x0000
一.问题 今天在将之前的STM32 LwIP1.4.1版本程序移植到2.1.2版本上时,发现ping不同,但是开发板有ICMP回复包,黄颜色警告checksum为0x0000.说明LwIP移植应该是没 ...
- .vscode\settings.json .gitignore 项目文件配置
一.项目本地新增配置文件: .vscode\settings.json { // editor "editor.formatOnSave": true, "e ...
- Hamming(汉明)窗的原理介绍及实例解析
概念 在数字信号处理过程中,每次FFT变换只能对有限长度的时域数据进行变换,因此,需要对时域信号进行信号截断.即使是周期信号,如果截断的时间长度不是周期的整数倍(周期截断),那么,截取后的信号将会 ...