OpenCV 中的快速颜色量化

2024-01-21

如何使用 OpenCV (+ C++) 以最快的方式减少图像中不同颜色的数量?我不想要完整的代码。我已经使用 kmeans 做到了,但速度不是很快。这是我的代码中速度较慢的部分:

kmeans(samples, clusterCount, labels,
    TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 10.0),
    1, KMEANS_RANDOM_CENTERS, centers);

这段代码需要几秒钟的时间来处理,这对我来说非常非常慢。我为此使用了Matlab(rgb2ind)速度很快。差不多0.01秒。

我想将我的代码用于生产,用户期望程序速度快。

除了 kmeans 之外还有什么颜色量化的替代方案吗?有什么方法可以更快地运行 kmeans (我不这么认为,因为我尝试了许多不同的参数)?

Edit:
事实证明,颜色量化是一个非常复杂的主题,需要时间来编写一个良好的优化主题。我决定使用Magick++ (ImageMagick API)为了这。
因此,我还没有尝试过 Cris Luengo 的新(编辑)答案。但我将其标记为答案(也请查看评论),这样其他人就不会认为这个问题没有得到解答。


量化颜色的方法有很多。这里我描述四种。

均匀量化

在这里,我们使用具有均匀分布颜色的颜色图,无论它们是否存在于图像中。用 MATLAB 语言你可以这样写

qimg = round(img*(N/255))*(255/N);

将每个通道量化为N级别(假设输入范围为 [0,255]。您还可以使用floor,在某些情况下更适合。这导致N^3不同的颜色。例如与N=8您将获得 512 种独特的 RGB 颜色。

K-均值聚类

这是生成自适应调色板的“经典”方法。显然这将是最昂贵的。 OP 对所有像素的集合应用 k 均值。相反,k-means 可以应用于颜色直方图。过程是相同的,但您可能只有 32^3 = 33000 个数据点,而不是 1000 万个数据点(当今的典型图像)。当处理自然照片时,由减少箱数的直方图引起的量化在这里几乎没有影响。如果您要量化具有有限颜色集的图表,则不需要进行 k 均值聚类。

您只需一次遍历所有像素即可创建直方图。接下来,您运行常规 k 均值聚类,但使用直方图箱。每个数据点现在也有一个权重(该箱内的像素数),您需要考虑这一点。算法中确定聚类中心的步骤会受到影响。您需要计算数据点的加权平均值,而不是常规平均值。

结果受初始化的影响。

八叉树量化

八叉树是一种用于空间索引的数据结构,其中通过将每个轴切成两半,将卷递归地分为 8 个子卷。因此,该树由每个节点有 8 个子节点组成。对于颜色量化,RGB 立方体由八叉树表示,并计算每个节点的像素数(这相当于构建颜色直方图,并在其上构建八叉树)。接下来,删除叶节点,直到留下所需数量的叶节点。删除叶子节点一次发生 8 个,这样上一层的节点就成为叶子。有不同的策略来选择要修剪的节点,但它们通常围绕修剪像素数较低的节点进行。

这就是 Gimp 使用的方法。

因为八叉树总是从中间分割节点,所以它不像 k-means 聚类或 next 方法那么灵活。

最小方差量化

MATLAB的rgb2ind https://www.mathworks.com/help/matlab/ref/rgb2ind.htmlOP提到的,进行统一量化和他们所谓的“最小方差量化”:

最小方差量化将 RGB 颜色立方体切割成不同大小的较小框(不一定是立方体),具体取决于颜色在图像中的分布方式。

我不确定这意味着什么。这一页 https://www.mathworks.com/help/images/reduce-the-number-of-colors-in-an-image.html#f8-13153没有透露更多信息,但它有一个看起来像 RGB 立方体的 k-d 树分区的图形。 K-d 树是一种空间索引结构,它将空间数据递归地分成两半。在每一级别,您选择分离程度最高的维度,并沿该维度进行拆分,从而产生一个额外的叶节点。与八叉树相反,分裂可以发生在最佳位置,而不是节点的中间。

使用空间索引结构(k-d 树或八叉树)的优点是颜色查找非常快。您从根开始,根据 R、G 或 B 值做出二元决策,直到到达叶节点。无需像 k 均值那样计算到每个原型簇的距离。

[两周后编辑]我一直在考虑一个可能的实施方案,并且想出了一个 https://github.com/DIPlib/diplib/blob/master/src/segmentation/minimum_variance_partitioning.cpp。这是算法:

  • 全色直方图被视为分区。这将是 k-d 树的根,它现在也是叶节点,因为还没有其他节点。
  • 创建优先级队列。它包含k-d树的所有叶节点。优先级由分区沿一个轴的方差给出,如果我们要沿该轴分割分区,则减去两半的方差。选择分割位置以使两半的方差最小(使用大津算法)。也就是说,优先级越大,我们通过分割减少的总方差就越多。对于每个叶节点,我们计算每个轴的该值,并使用最大的结果。
  • We process partitions on the queue until we have the desired number of partitions:
    • 我们沿着轴并在确定优先级时计算的位置分割具有最高优先级的分区。
    • 我们计算两半的优先级,并将它们放入队列中。

当这样描述时,这是一个相对简单的算法,the code https://github.com/DIPlib/diplib/blob/master/src/segmentation/minimum_variance_partitioning.cpp有点复杂,因为我试图使其高效但通用。

比较

在 256x256x256 RGB 直方图上,我得到了比较 k 均值聚类和这个新算法的时间:

# clusters kmeans (s) minvar (s)
5 3.98 0.34
20 17.9 0.48
50 220.8 0.59

请注意,随着簇数量的增加,k 均值需要更多的迭代,因此时间呈指数增长。通常情况下,人们不会使用这么大的直方图,我想要拥有大数据以使计时更加稳健。

以下是将这三种方法应用于测试图像的示例:

Input:

制服与N=4导致多达 64 种不同的颜色 [与N=2得到8种不同的颜色并与其他方法进行比较,结果非常难看]:

K-means 有 8 种颜色:

新的“最小方差”有 8 种颜色:

与 K 均值结果相比,我更喜欢最后一个结果,尽管它们非常相似。


该程序说明了如何使用进行颜色量化DIPlib https://diplib.org及其最小方差划分的实现:

#include "diplib.h"
#include "dipviewer.h"
#include "diplib/simple_file_io.h"
#include "diplib/histogram.h"
#include "diplib/segmentation.h"
#include "diplib/lookup_table.h"

int main() {
   dip::Image input = dip::ImageRead( "/Users/cris/dip/images/flamingo.tif" );
   input.SetColorSpace( "RGB" ); // This image is linear RGB, not sRGB as assumed when reading RGB TIFFs.

   // Compute the color histogram.
   dip::Histogram hist( input, {}, { dip::Histogram::Configuration( 0.0, 255.0, 64 ) } );

   // Cluster the histogram, the output histogram has a label assigned to each bin.
   // Each label corresponds to one of the clusters.
   dip::uint nClusters = 8;
   dip::Image histImage = hist.GetImage(); // Copy with shared data
   dip::Image tmp;
   dip::CoordinateArray centers = dip::MinimumVariancePartitioning( histImage, tmp, nClusters );
   histImage.Copy( tmp ); // Copy 32-bit label image into 64-bit histogram image.

   // Find the cluster label for each pixel in the input image.
   dip::Image labels = hist.ReverseLookup( input );

   // The `centers` array contains histogram coordinates for each of the centers.
   // We need to convert these coordinates to RGB values by multiplying by 4 (=256/64).
   // `centers[ii]` corresponds to label `ii+1`.
   dip::Image lutImage( { nClusters + 1 }, 3, dip::DT_UINT8 );
   lutImage.At( 0 ) = 0; // label 0 doesn't exist
   for( dip::uint ii = 0; ii < nClusters; ++ii ) {
      lutImage.At( ii + 1 ) = { centers[ ii ][ 0 ] * 4, centers[ ii ][ 1 ] * 4, centers[ ii ][ 2 ] * 4 };
   }

   // Finally, we apply our look-up table mapping, painting each label in the image with
   // its corresponding RGB color.
   dip::LookupTable lut( lutImage );
   dip::Image output = lut.Apply( labels );
   output.SetColorSpace( "RGB" );

   // Display
   dip::viewer::ShowSimple( input, "input image" );
   dip::viewer::ShowSimple( output, "output image" );
   dip::viewer::Spin();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenCV 中的快速颜色量化 的相关文章

  • 比较两个直方图

    对于一个小型项目 我需要将一张图像与另一张图像进行比较 以确定图像是否大致相同 这些图像很小 宽度从 25 到 100 像素不等 这些图像应该具有相同的图片数据 但略有不同 因此简单的像素相等检查不起作用 考虑以下两种可能的情况 博物馆中的
  • 表达式访问者仅为某些 lambda 表达式调用 VisitParameter

    我希望能够使用嵌套扩展方法将 EF 中的实体投影到相应的视图模型 参见我之前的问题使用扩展方法在 EF 中投影单个实体 https stackoverflow com questions 39585427 projection of sin
  • .NET 中的 Class.forName() 等效项?

    动态获取对象类型然后创建它的新实例的 C 方法是什么 例如 如何在 C 中实现以下 Java 代码的结果 MyClass x MyClass Class forName classes MyChildClass newInstance Lo
  • 为什么 ObservableCollection 有两个集合构造函数?

    The 可观察集合 T https msdn microsoft com en us library ms668604类有两个构造函数 可以在其中传递项目集合 一个构造函数接受一个IEnumerable T 另一个List T 鉴于List
  • 将视频上传/保存到数据库或文件系统

    我以前从未尝试过保存视频 所以我对此了解不多 我知道如果视频很小 我可以转换为字节数组并保存到数据库 但是为了提高效率 我想了解如何将任何上传的视频保存到我的服务器文件中 然后只保存该文件的文件路径我的数据库表中的视频 我完全不知道如何开始
  • 如何在Unity Inspector中创建多维数组?

    如何在 Unity Inspector 中创建枚举多维数组并使其可序列化 以便我可以从不同的脚本调用它 public enum colors red blue green yellow cyan white purple public in
  • 为什么我收到编译错误“使用已删除的函数 'std::unique_ptr ...”

    我收到一条巨大的编译错误消息 c mingw include c 6 1 0 bits predefined ops h 123 18 error use of deleted function std unique ptr lt Tp D
  • Web浏览器控件:如何捕获文档事件?

    我正在使用 WPF 的 WebBrowser 控件加载一个简单的网页 在这个页面上我有一个锚点或一个按钮 我想在我的应用程序后面的代码中 即在 C 中 捕获该按钮的单击事件 WebBrowser 控件是否有办法捕获加载页面元素上的单击事件
  • _MM_TRANSPOSE4_PS 在 GCC 中导致编译器错误?

    我第一次在 GCC 而不是 MSVC 中编译我的数学库 并经历了所有的小错误 我遇到了一个根本没有意义的错误 Line 284 error lvalue required as left operand of assignment 284号
  • 如何解析多态 JSON 数组?

    我有一个 JSON 格式的文件 其中包含个人用户的记录 一些用户的记录中间有一个评论字段 我只想解析顶级项目 全名 贡献者姓名 电子邮件 使用 Newtonsoft JSON 解析器 但我似乎无法让它识别单个对象 当我将整个字符串解析为一个
  • 使用信号和槽更新指针

    我对 Qt 很陌生 请帮我解决这个问题 我正在使用线程在后台执行密集操作 同时我想更新 UI 所以我使用 SIGNALS 和 SLOTS 为了更新 UI 我发出一个信号并更新 UI 让我们考虑下面的示例代码 struct sample QS
  • 将旧的 Unity 代码升级到 Unity 5

    在触发按钮上播放动画的代码似乎不起作用 我在 Youtube 上看到了一个视频 内容很简单animation Play 它可以在该视频上运行 但我无法让它在我的计算机上运行 我做错了什么还是团结改变了它 请帮助我在网上找不到解决方案 所有
  • 如何将 Boost Spirit 自动规则与 AST 结合使用?

    编辑 当我想在另一个规则上使用它时 我扩展了 sehe 的示例以显示问题 http liveworkspace org code 22lxL7 http liveworkspace org code 22lxL7 17 我正在尝试提高 Bo
  • 如何在realm-dotnet中存储System.Collections.Generic.Dictionary

    我正在尝试将 Realm NET 集成到我的 uwp 项目中 我想知道是否有任何方法可以在 Realm dotnet 库中存储 System Collections Generic Dictionary 我试过这个 public class
  • 为什么我无法通过 lambda 捕获“this”指针?

    考虑以下代码 class A public void foo auto functor this A a this auto functor a The compiler won t accept this instead of a a g
  • 使用 DataGridViewCheckboxCell 真正禁用 DataGridView 中的复选框

    有谁知道如何使用 DataGridViewCheckboxCell 禁用 DataGridView 中的复选框 我可以将其设置为只读 并设置背景颜色 但我无法让复选框本身显示为禁用状态 有什么想法吗 Guess 你必须自己画 http so
  • 从数据库配置中的连接字符串中删除 SSIS 密码

    我有一个 SSIS 包 它使用 SQL 服务器中的 SSIS 配置表来检索 OLE DB 连接管理器的连接字符串属性 问题是我还需要相同的连接字符串来调用使用实体框架的程序集 我尝试访问连接管理器连接字符串属性 但 SSIS 总是删除密码
  • 打印任何类型的数组和列表的通用方法[重复]

    这个问题在这里已经有答案了 每当我调试一段涉及整数 双精度 字符串等数组或列表的代码时 有时我更喜欢打印它们 我为此所做的是为不同类型编写重载的 printArray printList 方法 for e g 我可能有这 3 种方法来打印各
  • 小数精度

    我使用小数类型进行高精度计算 货币 但我今天遇到了这个简单的划分 1 1 37 这应该再次得到 37 http www wolframalpha com input i 1 2F 281 2F37 29 http www wolframal
  • 推断“x => { throw .. }”的 Lambda 与重载方法中的 Func 匹配吗?

    我不明白为什么 C 最终在以下 LINQPad 代码中执行不正确的扩展方法 void Main Actual Sync Action Expected Sync Action Run x gt x Dump Actual Async Tas

随机推荐

  • TomEE CDI @Inject NullPointerException

    我试图让 CDI 在我的应用程序中工作 但是当我这样做时 我只得到 NullPointerExceptions 当正常实例化 playlistService 时 它 工作得很好 但是当我尝试使用 CDI 时 它就不再工作了 服务器能够启动
  • 让 2 个不同的父实体通过 JPA 中的 @OneToMany 引用子实体

    我有一个有点奇怪的问题 我不知道JPA是否支持这个 我有一个 Entity Child和另外两个实体 Entity Parent1 and Entity Parent2 我想做的是有一个 OneToMany父母和孩子之间的关系 以及另一个
  • 使用 Google datalab 读取文件

    我正在尝试使用 Google Datalab 读取 ipython 笔记本中的文件 基本的 pd read csv 因为我找不到文件的路径 我把它放在本地 并将其上传到谷歌云存储的一个桶中 我运行了以下命令来了解我在哪里 os getcwd
  • 调用外部意图(相机)时调试器断开连接

    我正在尝试调试我的应用程序 因为从我调用相机意图到拍摄照片时发生了一些事情 并且我的应用程序在返回时强制关闭 问题是 当调用相机意图时 调试器会立即断开连接 并且我无法再调试我的应用程序 我正在设备上调试 而不是模拟器上 我在谷歌中发现了这
  • 将图像添加到 JavaFX 特定位置的按钮

    当我向按钮添加图像和文本时 默认情况下元素是水平设置的 如何更改此行为以在图像下获取文本 Set the 内容显示属性 http docs oracle com javafx 2 api javafx scene control Label
  • gitk 中“标记此提交”是什么意思?

    使用 gitk 时 可以在日志窗格中选择提交 右键单击上下文菜单会提供 标记此提交 那有什么作用 查看以下选项 返回标记 找到它的后代并标记 与标记的提交进行比较
  • iOS 8 + 交互式过渡 + 显示的导航栏 = 损坏?

    我们正在尝试在我们的应用程序中添加两个视图之间的交互式过渡 我们可以让它在 iOS 7 或 iOS 8 上正常工作不显示导航栏 但是 我们需要显示一个导航栏 为了演示我们遇到的 一些 问题 我创建了一个小型原型 屏幕截图如下所示 我尝试了
  • 限制 unserialize() 返回数组?

    有没有办法限制 PHP 的 unserialize 只解析数组 出于安全原因 假设在未序列化的对象中有一个邪恶的 unserialize 魔术方法 我不想调用 有没有办法限制 PHP 的 unserialize 只解析数组 出于安全原因 假
  • Scala Future 带有过滤器以供理解

    在下面的示例中我得到了异常java util NoSuchElementException Future filter predicate is not satisfied 我想要结果Future Test2 当检查if i 2 失败 如何
  • 如何将关系型数据库映射到OWL?

    我正在尝试将关系数据库映射到 OWL 这是我的 2 张桌子 学生 student id student name course id 课程 课程ID 课程名称 id name course id 1 Adam 5 2 Michael 2 c
  • PHP 函数以 & 符号开头是什么意思?

    我正在使用 Facebook 库 其中包含以下代码 class FacebookRestClient public function users hasAppPermission ext perm uid null return this
  • 显示:表格在 ie6 和 ie7 中不起作用 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我创建了一个菜单 但它在 ie6 和
  • XA 数据源的性能开销 - 最佳实践

    我试图了解 XA 数据源对性能的影响 在许多应用程序中 并非所有事务都需要参与分布式事务 意味着只有少数事务需要与其他资源一起分发 参与 性能权衡是否足够高 足以配置两个数据源 XA 和非 XA 各一个 同样 答案是 这取决于场景 但我正在
  • 在 Julia 中声明主函数/入口点

    是否有一种现成的或惯用的方法来声明 Julia 程序中的入口点 即相当于main在 C 或if name main 在Python中构造 这似乎是一个重要的功能 以便编写较大的结构化代码 这些代码不会在交互模式下使用 但我找不到任何关于如何
  • 如何将两个列表中的元素组合到第三个列表中?

    我有两个清单a and b a 3 6 8 65 3 b 34 2 5 3 5 c gets 3 34 6 2 8 5 65 3 3 5 是否有可能在Python中获得它们的比率 就像在变量中一样c above I tried a b并得到
  • 如何使 Perl 的 File::Find 更快?

    我有一个名为Lib我正在使用 File Find 模块在整个目录中搜索该文件夹 D 搜索需要很长时间 如果驱动器有很多子目录 甚至需要 5 分钟 我怎样才能更快地搜索该库 以便在几秒钟内完成 我的代码如下所示 find Lib files
  • 带有 SVG 图像的 CSS 光标指针

    我正在尝试将自定义光标指针与 SVG 图像一起应用 但它不起作用 相反 如果我使用 png 图像 则一切正常 这是我的代码 container not working one cursor url images icon cross svg
  • 如何删除“打开应用程序 tumblr”消息?

    我对使用 tumblr 写博客感兴趣 并且发现了一个令人惊叹的响应式主题 但我想知道是否可以删除 在 Tumblr 应用程序中打开 消息 以及如何做到这一点 打开应用程序tumblr https i stack imgur com T1K4
  • 在 Android 中将 Parcelable 对象存储到文件中

    我试图将 ResolveInfo 对象的 ArrayList 存储到文件中 这样我就不必在每次应用程序启动时重建它 大约 4 6 秒 ResolveInfo 对象是可 Parcelable 但不是可序列化的 所以我得到一个 java io当
  • OpenCV 中的快速颜色量化

    如何使用 OpenCV C 以最快的方式减少图像中不同颜色的数量 我不想要完整的代码 我已经使用 kmeans 做到了 但速度不是很快 这是我的代码中速度较慢的部分 kmeans samples clusterCount labels Te