C++ 检查图像颜色的更快算法
假设给我一张2048x2048的图像,我想知道图像中存在的颜色总数,那么最快的算法是什么?我提出了两种算法,但它们都很慢 算法1:C++ 检查图像颜色的更快算法,c++,visual-c++,C++,Visual C++,假设给我一张2048x2048的图像,我想知道图像中存在的颜色总数,那么最快的算法是什么?我提出了两种算法,但它们都很慢 算法1: 比较当前像素和下一个像素,以及它们是否不同 检查包含所有检测到的颜色的临时变量,以查看颜色是否存在 如果不存在,则将其添加到数组(列表)并增加noOfColors 这个算法有效,但速度很慢。对于1600x1200像素的图像,大约需要3秒 算法2: 将每个像素与所有其他像素一起检查,记录颜色出现次数并增加计数的明显方法。这是非常非常慢,几乎像一个挂起的应用程序。那
- 比较当前像素和下一个像素,以及它们是否不同
- 检查包含所有检测到的颜色的临时变量,以查看颜色是否存在
- 如果不存在,则将其添加到数组(列表)并增加noOfColors李>
将每个像素与所有其他像素一起检查,记录颜色出现次数并增加计数的明显方法。这是非常非常慢,几乎像一个挂起的应用程序。那么有没有更好的方法?我需要所有的像素信息。你可以使用(或),只需在像素上做一个循环,将颜色添加到集合中。然后颜色的数量就是集合的大小。好的,这适合于并行化。将图像分割为几个部分,并在单独的任务中为每个部分执行算法。为了避免同步,每种颜色都应该有自己的存储空间。所有任务完成后,您将聚合结果。这里唯一可行的算法是建立一种图像颜色的直方图。在你的例子中唯一的不同是,你不需要计算每种颜色的总体,你只需要知道它是否为零 根据您使用的颜色空间的不同,您可以使用
std::set
标记现有颜色(正如Joachim Pileborg所建议的),或者只使用类似std::bitset
的东西,这显然更快。这取决于颜色空间中存在多少不同的颜色
此外,正如Marius Bancila所指出的,这个过程非常适合并行化。计算图像部分的直方图数据,然后将其合并。当然,图像分割应该基于其内存分区,而不是几何特性。简单地说,垂直分割图像(按扫描线的批次),而不是水平分割
而且,如果可能的话,您应该使用一些低级库/代码来运行像素,或者尝试编写自己的。至少你必须获取一个指向扫描线的指针,并在扫描线的像素上批量运行,而不是对每个像素执行类似于GetPixel的操作。这里的要点是,图像作为2D颜色数组的理想表示方式并不是图像存储在内存中的方式(颜色组件可以排列在“平面”中,可能有“填充”等。因此使用类似于
GetPixel
的函数获取像素可能需要时间
因此,如果图像不是“矢量绘制”的结果,那么这个问题甚至可能毫无意义:想象一张照片:在附近的两个“绿色”之间,你会发现所有的绿色阴影,因此颜色——在这种情况下——不再是图像本身编码所支持的颜色(2^24,或256,或16或……),因此,除非你对颜色分布感兴趣(它们的使用方式有多不同),否则仅仅计算它们是没有意义的
解决方法可以是:
位集的索引
:
bitset<256*256*256> colours;
for(int pixel: pixels) {
colours[pixel] = true;
}
colours.count();
位集颜色;
用于(整数像素:像素){
颜色[像素]=真;
}
颜色。计数();
这具有线性复杂性。DRAM非常便宜。使用蛮力。填写选项卡,计数 在3.0GHz的core2duo上: 4096x4096 32位rgb为0.35秒 在一些琐碎的并行化之后0.20秒(我对omp一无所知) 但是,如果要使用64位rgb(一个通道=16位),则是另一个问题(内存不足)。 您可能需要一个好的哈希表函数。 使用随机像素,相同大小需要10秒 备注:在0.15秒时,std::bitset解决方案的速度更快(它变得更慢,只是简单的并行化!) 解决方案,c++11
#include <vector>
#include <random>
#include <iostream>
#include <boost/chrono.hpp>
#define _16M 256*256*256
typedef union {
struct { unsigned char r,g,b,n ; } r_g_b_n ;
unsigned char rgb[4] ;
unsigned i_rgb;
} RGB ;
RGB make_RGB(unsigned char r, unsigned char g , unsigned char b) {
RGB res;
res.r_g_b_n.r = r;
res.r_g_b_n.g = g;
res.r_g_b_n.b = b;
res.r_g_b_n.n = 0;
return res;
}
static_assert(sizeof(RGB)==4,"bad RGB size not 4");
static_assert(sizeof(unsigned)==4,"bad i_RGB size not 4");
struct Image
{
Image (unsigned M, unsigned N) : M_(M) , N_(N) , v_(M*N) {}
const RGB* tab() const {return & v_[0] ; }
RGB* tab() {return & v_[0] ; }
unsigned M_ , N_;
std::vector<RGB> v_;
};
void FillRandom(Image & im) {
std::uniform_int_distribution<unsigned> rnd(0,_16M-1);
std::mt19937 rng;
const int N = im.M_ * im.N_;
RGB* tab = im.tab();
for (int i=0; i<N; i++) {
unsigned r = rnd(rng) ;
*tab++ = make_RGB( (r & 0xFF) , (r>>8 & 0xFF), (r>>16 & 0xFF) ) ;
}
}
size_t Count(const Image & im) {
const int N = im.M_ * im.N_;
std::vector<char> count(_16M,0);
const RGB* tab = im.tab();
#pragma omp parallel
{
#pragma omp for
for (int i=0; i<N; i++) {
count[ tab->i_rgb ] = 1 ;
tab++;
}
}
size_t nColors = 0 ;
#pragma omp parallel
{
#pragma omp for
for (int i = 0 ; i<_16M; i++) nColors += count[i];
}
return nColors;
}
int main() {
Image im(4096,4096);
FillRandom(im);
typedef boost::chrono::high_resolution_clock hrc;
auto start = hrc::now();
std::cout << " # colors " << Count(im) << std::endl ;
boost::chrono::duration<double> sec = hrc::now() - start;
std::cout << " took " << sec.count() << " seconds\n";
return 0;
}
#包括
#包括
#包括
#包括
#定义_16m256*256*256
typedef联合{
结构{无符号字符r,g,b,n;}r_g_b_n;
无符号字符rgb[4];
未签名的i_rgb;
}RGB;
RGB make_RGB(无符号字符r、无符号字符g、无符号字符b){
RGB res;
res.r_g_b_n.r=r;
res.r_g_b_n.g=g;
res.r_g_b_n.b=b;
res.r_g_b_n.n=0;
返回res;
}
静态断言(sizeof(RGB)==4,“错误的RGB大小不是4”);
static_assert(sizeof(unsigned)=4,“坏i_RGB大小不是4”);
结构映像
{
图像(无符号M,无符号N):M(M),N(N),v(M*N){
常量RGB*tab()常量{return&v_u0];}
RGB*制表符(){return&v_[0];}
无符号M_uu,N_u;
std::向量v_;
};
无效填充随机(图像和im){
标准:均匀分布rnd(0,_16M-1);
标准:mt19937 rng;
常数int N=im.M_*im.N;
RGB*tab=im.tab();
对于(inti=0;i>8&0xFF),(r>>16&0xFF));
}
}
大小\u t计数(常量图像和im){
常数int N=im.M_*im.N;
标准:病媒计数(_16M,0);
常量RGB*tab=im.tab();
#pragma-omp并行
char * creatematcharray(struct rgb_color *palette, int palettesize)
{
int rval=16, gval=16, bval=16, len, r, g, b;
char *taken, *match, *same;
int i, set, sqstep, tp, maxtp, *entryr, *entryg, *entryb;
char *table;
len=rval*gval*bval;
// Prepare table buffers:
size_t size_of_table = len*sizeof(char);
table=(char *)malloc(size_of_table);
if (table==nullptr) return nullptr;
// Select colors to use for fill:
set=0;
size_t size_of_taken = (palettesize * sizeof(int) * 3) +
(palettesize*sizeof(char)) + (len * sizeof(char));
taken=(char *)malloc(size_of_taken);
same=taken + (len * sizeof(char));
entryr=(int*)(same + (palettesize * sizeof(char)));
entryg=entryr + palettesize;
entryb=entryg + palettesize;
if (taken==nullptr)
{
free((void *)table);
return nullptr;
}
std::memset((void *)taken, 0, len * sizeof(char));
// std::cout << "sizes: " << size_of_table << " " << size_of_taken << std::endl;
match=table;
for (i=0; i<palettesize; i++)
{
same[i]=0;
// Compute 3d-table coordinates of palette rgb color:
r=palette[i].r&0x0f, g=palette[i].g&0x0f, b=palette[i].b&0x0f;
// Put color in position:
if (taken[b*rval*gval+g*rval+r]==0) set++;
else same[match[b*rval*gval+g*rval+r]]=1;
match[b*rval*gval+g*rval+r]=i;
taken[b*rval*gval+g*rval+r]=1;
entryr[i]=r; entryg[i]=g; entryb[i]=b;
}
// @@@ Fill match_array by steps: @@@
for (set=len-set, sqstep=1; set>0; sqstep++)
{
for (i=0; i<palettesize && set>0; i++)
if (same[i]==0)
{
// Fill all six sides of incremented cube (by pairs, 3 loops):
for (b=entryb[i]-sqstep; b<=entryb[i]+sqstep; b+=sqstep*2)
if (b>=0 && b<bval)
for (r=entryr[i]-sqstep; r<=entryr[i]+sqstep; r++)
if (r>=0 && r<rval)
{ // Draw one 3d line:
tp=b*rval*gval+(entryg[i]-sqstep)*rval+r;
maxtp=b*rval*gval+(entryg[i]+sqstep)*rval+r;
if (tp<b*rval*gval+0*rval+r)
tp=b*rval*gval+0*rval+r;
if (maxtp>b*rval*gval+(gval-1)*rval+r)
maxtp=b*rval*gval+(gval-1)*rval+r;
for (; tp<=maxtp; tp+=rval)
if (!taken[tp])
taken[tp]=1, match[tp]=i, set--;
}
for (g=entryg[i]-sqstep; g<=entryg[i]+sqstep; g+=sqstep*2)
if (g>=0 && g<gval)
for (b=entryb[i]-sqstep; b<=entryb[i]+sqstep; b++)
if (b>=0 && b<bval)
{ // Draw one 3d line:
tp=b*rval*gval+g*rval+(entryr[i]-sqstep);
maxtp=b*rval*gval+g*rval+(entryr[i]+sqstep);
if (tp<b*rval*gval+g*rval+0)
tp=b*rval*gval+g*rval+0;
if (maxtp>b*rval*gval+g*rval+(rval-1))
maxtp=b*rval*gval+g*rval+(rval-1);
for (; tp<=maxtp; tp++)
if (!taken[tp])
taken[tp]=1, match[tp]=i, set--;
}
for (r=entryr[i]-sqstep; r<=entryr[i]+sqstep; r+=sqstep*2)
if (r>=0 && r<rval)
for (g=entryg[i]-sqstep; g<=entryg[i]+sqstep; g++)
if (g>=0 && g<gval)
{ // Draw one 3d line:
tp=(entryb[i]-sqstep)*rval*gval+g*rval+r;
maxtp=(entryb[i]+sqstep)*rval*gval+g*rval+r;
if (tp<0*rval*gval+g*rval+r)
tp=0*rval*gval+g*rval+r;
if (maxtp>(bval-1)*rval*gval+g*rval+r)
maxtp=(bval-1)*rval*gval+g*rval+r;
for (; tp<=maxtp; tp+=rval*gval)
if (!taken[tp])
taken[tp]=1, match[tp]=i, set--;
}
}
}
free((void *)taken);`enter code here`
return table;
}
#define USEHASH 1
#ifdef USEHASH
#include <unordered_map>
#endif
size = im->xw * im->yw;
#ifdef USEHASH
// unordered_map is about twice as fast as map on my mac with qt5
// --------------------------------------------------------------
#include <unordered_map>
std::unordered_map<qint64, unsigned char> colors;
colors.reserve(size); // pre-allocate the hash space
#else
std::map<qint64, unsigned char> colors;
#endif
for (i=0; i<size; i++)
{
pel = BUILDPEL(i); // macro just shovels 0RGB into 64 bit pel from im
// You'd do the same for your image structure
// in whatever way is fastest for you
colors[pel] = 1;
}
cc = colors.size();
// time here: 14 secs for map, 7 secs for unordered_map with
// 12,106,244 pixels containing 11,857,131 colors on 12/24 core,
// 3 GHz, 64GB machine.