书接上文 一种基于DeltaE(CIE 1976)的找色算法

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实现的更多相关文章

  1. 五种基于RGB色彩空间统计的皮肤检测算法

    最近一直在研究多脸谱识别以及如何分辨多个皮肤区域是否是人脸的问题 网上找了很多资料,看了很多篇文章,将其中基于RGB色彩空间识别皮肤 的统计算法做了一下总结,统计识别方法主要是简单相比与很多其它基于 ...

  2. 一种基于LQR使输出更加稳定的算法(超级实用)

    已知: 令: 则: 以上三式成立 具体步骤: 状态量最后一行加入“上一时刻的控制量”: A,B根据上述方法变形: Q,R增加维度(控制量一般都为一个,此时R维度不变): 最关键的是——输出量已经变为“ ...

  3. (转载)找圆算法((HoughCircles)总结与优化

      Opencv内部提供了一个基于Hough变换理论的找圆算法,HoughCircle与一般的拟合圆算法比起来,各有优势:优势:HoughCircle对噪声点不怎么敏感,并且可以在同一个图中找出多个圆 ...

  4. 转载-找圆算法((HoughCircles)总结与优化-霍夫变换

    原文链接: http://www.opencv.org.cn/forum.php?mod=viewthread&tid=34096   找圆算法((HoughCircles)总结与优化 Ope ...

  5. 找圆算法((HoughCircles)总结与优化

    http://www.opencv.org.cn/forum.php?mod=viewthread&tid=34096  Opencv内部提供了一个基于Hough变换理论的找圆算法,Hough ...

  6. [信安Presentation]一种基于GPU并行计算的MD5密码解密方法

    -------------------paper--------------------- 一种基于GPU并行计算的MD5密码解密方法 0.abstract1.md5算法概述2.md5安全性分析3.基 ...

  7. 一种基于 Numpy 的 TF-IDF 实现报告

    一种基于 Numpy 的 TF-IDF 实现报告 摘要 本文使用了一种 state-of-the-art 的矩阵表示方法来计算每个词在每篇文章上的 TF-IDF 权重(特征).本文还将介绍基于 TF- ...

  8. 26种基于PHP的开源博客系统

    26种基于PHP的开源博客系统 来源:本站原创 PHP学习笔记 以下列举的PHP开源Blog系统中,除了我们熟知的WordPress之外,大多都没有使用过,其中一些已经被淘汰,或者有人还在使用.除了做 ...

  9. Hive数据分析——Spark是一种基于rdd(弹性数据集)的内存分布式并行处理框架,比于Hadoop将大量的中间结果写入HDFS,Spark避免了中间结果的持久化

    转自:http://blog.csdn.net/wh_springer/article/details/51842496 近十年来,随着Hadoop生态系统的不断完善,Hadoop早已成为大数据事实上 ...

  10. LM-MLC 一种基于完型填空的多标签分类算法

    LM-MLC 一种基于完型填空的多标签分类算法 1 前言 本文主要介绍本人在全球人工智能技术创新大赛[赛道一]设计的一种基于完型填空(模板)的多标签分类算法:LM-MLC,该算法拟合能力很强能感知标签 ...

随机推荐

  1. 安装MySql失败( Microsoft Visual C++ 2013 Runtime 64bit)

    参考资料:下载之家 提示你缺少什么版本就安装什么版本.64位或者32位. 文件下载地址:下载之家 不知道有没有失效,如果失效的话大家直接去下载之家搜索下载.

  2. 面向开发者的 ChatGPT 提示工程课程|吴恩达携手OpenAI 教你如何编写 prompt

    提示工程(Prompt Engineering)是一门相对较新的学科,旨在开发和优化提示,从而高效地将语言模型(LM)用于各种应用和研究主题,并帮助开发人员更好地理解大型语言模型(LLM)的能力和局限 ...

  3. 【Azure 应用服务】Azure Function Timer触发函数加上Singleton后的问题

    问题描述 在Azure Function Timer Trigger的函数中,添加了Singleton属性,当Function的实例变为3个后,发现Timer函数并没有在三个实例上同时运行,每次触发时 ...

  4. 【Azure Redis 缓存】Redis导出数据文件变小 / 在新的Redis复原后数据大小压缩近一倍问题分析

    问题描述 使用 Azure Cache for Redis 服务,在两个Redis服务之间进行数据导入和导出测试.在Redis中原本有7G的数据值,但是导出时候发现文件大小仅仅只有30MB左右,这个压 ...

  5. docker知识整理(备份)

    概念: 镜像:Docker 镜像类似于虚拟机镜像,可以将它理解为一个只读的模板 .镜像是创建 Docker 容器的基础.通过版本管理和增量的文件系统, Docker 提供了一套十分简单的机制来创建和更 ...

  6. 如何避免MYSQL主从延迟带来的读写问题?

    在MYSQL 部署架构选型上,许多公司都会用到主从读写分离的架构,如下是一个一主一从的架构,主库master负责写入,从库slave进行读取. 但是既然是读写分离,必然会面临这样一个问题,当在主库上进 ...

  7. vscode中输入``自动将光标后面一个单词选中,左右加入<w>和</w>标签 - snippets 的命令调用

    需求 vscode中输入``自动将光标后面一个单词选中,左右加入和标签 步骤0 准备需要安装插件 vim - 这里的点击两次按键激活的快捷键,这个插件可以设置 macros - 一次执行多个命令的插件 ...

  8. 写了一个 dict.cn 的油猴脚本,目的是通过url进行搜索。这样就能配合wox进行单词的搜索了。

    写了一个 dict.cn 的油猴脚本,目的是通过url进行搜索.这样就能配合wox进行单词的搜索了. // ==UserScript== // @name dict.cn // @namespace ...

  9. 利用log4j打印sql的log日志

    默认情况下,使用ibatis是不打印ibatis相关的log的,因为内部的sql执行都是内部调用,在server的控制台是不 会 打印log的. 在log4j的配置文件log4j.properties ...

  10. 大年学习linux(第六节---软件安装)

    六.软件安装 rpm RPM软件包的管理工具 补充说明 rpm命令 是RPM软件包的管理工具.rpm原本是Red Hat Linux发行版专门用来管理Linux各项套件的程序,由于它遵循GPL规则且功 ...