一:基本思路

对于一张RGB色彩空间的彩色图像,很多时间我们想通过程序获得该图像有几种主要的色彩,但是对一般图像来说,在色彩交界处都是通过像素混合来实现自然过渡,所以直接扫描图像的像素值,得到的不同颜色值可能多达上百中,而实际上图像可能只有3~4种的主要色彩,如何去掉那些混合颜色,准确提取出来这3~4中的主色彩,根据一般图像的特征,图像在不同色彩的边界处混合不同的颜色值,此可以视为图像的边缘特性之一,因此可以根据简单的边缘梯度算法实现这些混合像素的提取得到输出的像素值数组,然后扫描每个像素值,寻找指定半径参数R周围的像素,发现为零,而且距离中心像素最近的像素点的值做为中心像素的像素值,扫描结束以后输出像素数组,然后对数组线性扫描,即可得到图片的主要色彩RGB值。

二:实现步骤

1.      输入图像数组,对彩色图像灰度化

2.      对灰度化以后的图像,计算图像梯度,这里使用sobol算子

3.      对得到每一个非零像素点实现半径为R的范围内的扫描,找出与之最相近的为零的原像素值

4.      对得到数组进行简单的扫描,得到主色彩

其中参数R是要根据不同应用场景,找到最合适的值。理论上图像越大,R的取值也应该越大,否则算法会失准。

三:原图及运行效果

原图

算法运行之后提取到四种主要色彩

四:算法实现源代码

public static BufferedImage removeBlendPixels(BufferedImage image, int raidus) {int width = image.getWidth();int height = image.getHeight();int[] pixels = new int[width * height];getRGB(image, 0, 0, width, height, pixels);// 创建处理结果BufferedImage resultImg = createCompatibleDestImage(image, null);setRGB(resultImg, 0, 0, width, height, pixels);// 灰度化与梯度求取byte[] grayData = getGrayData(pixels, width, height);byte[] binaryData = getGrident(grayData, width, height);int index = 0;for (int row = 1; row < height - 1; row++) {for (int col = 1; col < width - 1; col++) {index = row * width + col;int pixel = (binaryData[index] & 0xff);if (pixel > 0) {// 半径扫描操作int mindis = Integer.MAX_VALUE;int minrow = -1;int mincol = -1;int nr = 0;int nc = 0;int index2 = 0;for (int subrow = -raidus; subrow <= raidus; subrow++) {nr = row + subrow;if (nr < 0 || nr >= height) {continue;}for (int subcol = -raidus; subcol <= raidus; subcol++) {nc = col + subcol;if (nc < 0 || nc >= width) {continue;}index2 = nr * width + nc;int value = (binaryData[index2] & 0xff);if (value == 0) {int distance = distanceColor(image.getRGB(nc, nr), image.getRGB(col, row));if (distance < mindis) {mindis = distance;minrow = nr;mincol = nc;}}}}resultImg.setRGB(col, row, image.getRGB(mincol, minrow));}}}return resultImg;}public static int distanceColor(int rgb, int rgb2) {// Color oneint r1 = (rgb >> 16) & 0xff;int g1 = (rgb >> 8) & 0xff;int b1 = rgb & 0xff;// Color twoint r2 = (rgb2 >> 16) & 0xff;int g2 = (rgb2 >> 8) & 0xff;int b2 = rgb2 & 0xff;// distanceint rr = r1 - r2;int gg = g1 - g2;int bb = b1 - b2;int sum = (int) Math.sqrt(rr * rr + gg * gg + bb * bb);return sum;}public static byte[] getGrayData(int[] inPixels, int width, int height) {// 图像灰度化byte[] outPixels = new byte[width * height];int index = 0;for (int row = 0; row < height; row++) {int tr = 0, tg = 0, tb = 0;for (int col = 0; col < width; col++) {index = row * width + col;tr = (inPixels[index] >> 16) & 0xff;tg = (inPixels[index] >> 8) & 0xff;tb = inPixels[index] & 0xff;int gray = (int) (0.299 * tr + 0.587 * tg + 0.114 * tb);outPixels[index] = (byte) (gray & 0xff);}}return outPixels;}public static byte[] getGrident(byte[] inPixels, int width, int height) {byte[] outPixels = new byte[width * height];int index = 0;for (int row = 0; row < height; row++) {int tr = 0;for (int col = 0; col < width; col++) {if (row == 0 || col == 0 || (row == height - 1) || (col == width - 1)) {index = row * width + col;outPixels[index] = (byte) (0x00);continue;}int xg = 0, yg = 0;for (int sr = -1; sr <= 1; sr++) {for (int sc = -1; sc <= 1; sc++) {int nrow = row + sr;int ncol = col + sc;if (nrow < 0 || nrow >= height) {nrow = 0;}if (ncol < 0 || ncol >= width) {ncol = 0;}index = nrow * width + ncol;tr = (inPixels[index] & 0xff);xg += X_SOBEL[sr + 1][sc + 1] * tr;yg += Y_SOBEL[sr + 1][sc + 1] * tr;}}index = row * width + col;int g = (int) Math.sqrt(xg * xg + yg * yg);outPixels[index] = (byte) (clamp(g) & 0xff);}}return outPixels;}

需要定义的常量值如下:

 public static final int[][] X_SOBEL = new int[][] { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } };public static final int[][] Y_SOBEL = new int[][] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };public static final int BLOCK_PIXEL_RADIUS = 5;

梯度求取使用是sobol算子,对处理以后的BufferedImage对象扫描获取主色彩的代码如下:

     int width = result.getWidth();int height = result.getHeight();Map<Integer, Integer> colorIndexMap = new HashMap<Integer, Integer>();for (int row = 0; row < height; row++) {for (int col = 0; col < width; col++) {int pixelValue = result.getRGB(col, row);if (!colorIndexMap.containsKey(pixelValue)) {colorIndexMap.put(pixelValue, pixelValue);}}}// now scan pixel value// return resultSystem.out.println("number of color = " + colorIndexMap.size());return colorIndexMap.keySet().toArray(new Integer[0]);

测试代码如下:

 public static void main(String[] args) {File file = new File("D:\\gloomyfish\\bigmonkey.png");File resultFile = new File("D:\\gloomyfish\\result.png");try {BufferedImage image = ImageIO.read(file);BufferedImage result = removeBlendPixels(image, BLOCK_PIXEL_RADIUS);ImageIO.write(result, "png", resultFile);Integer[] colors = extractColors(result);System.out.println("total colors : " + colors.length);} catch (IOException e) {e.printStackTrace();}}

注意:主要的关键在于对待处理图像输入正确大小的半径,这个半径大小跟图像实际大小相关,而且算法可以近一步优化成不依赖半径参数的版本,知道找到等于零的像素为止。

如何获取彩色图像中的主色彩相关推荐

  1. java获取图片主色_Java获取彩色图像中的主色彩的实例代码

    本文讲述了java获取彩色图像中的主色彩的实例代码.分享给大家供大家参考,具体如下: 一:基本思路 对于一张rgb色彩空间的彩色图像,很多时间我们想通过程序获得该图像有几种主要的色彩,但是对一般图像来 ...

  2. js获取url中的主域名

    var url = "http://support.supermap.com.cn/DataWarehouse/WebDocHelp/iMobileLiteFor"; var do ...

  3. 获取GridView中RowCommand的当前选中行的索引或主键Id

    获取GridView中RowCommand的当前索引行 前台添加一模版列,里面添加一个LinkButton 前台 (如果在后台代码中用e.CommandArgument取值的话前台代码就必须在按钮中设 ...

  4. JDBC中事务、批量操作、大数据类型、获取自动生成的主键、等用法

    1 事务的用法 事务的ACID属性: 通俗的说事务:指一组操作,要么都成功执行,要么都不执行-->原子性在所有的操作没有执行完毕之前,其他会话不能够看到中间改变的过程-->隔离性事务发生前 ...

  5. 3、JavaWeb中Service层的作用、MyBatis的重要组件、mybatis-config.xml中的别名映射、properties配置、#{}和${}的区别、获取插入数据的主键值

    文章目录 1.Service层的作用 2.MyBatis重要组件 Resources SqlSessionFactoryBuilder SqlSessionFactory SqlSession 针对上 ...

  6. server sql 数据总行数_SqlServer中获取数据库中每个表的行数

    CREATE TABLE #RowCounts(NumberOfRows BIGINT,TableName VARCHAR(128)) EXEC sp_MSForEachTable 'INSERT I ...

  7. 主键SQL教程–如何在数据库中定义主键

    Every great story starts with an identity crisis. Luke, the great Jedi Master, begins unsure - " ...

  8. mysql删除没有索引页_InnoDB中没有主键是如何运转的

    InnoDB聚簇索引的背景 在InnoDB索引页的物理结构中,"我"讲述了"InnoDB中一切都是索引".这意味着每个InnoDB引擎的表必须有一个" ...

  9. Mybatis 在 insert 之后想获取自增的主键 id,但却总是返回1

    记录一次傻逼的问题, 自己把自己蠢哭:Mybatis 在 insert 之后想获取自增的主键 id,但却总是返回1 错误说明: 返回的1是影响的行数,并不是自增的主键id: 想要获取自增主键id,需要 ...

最新文章

  1. 第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛 L-回旋星空
  2. Windows下配置单机Hadoop环境
  3. c语言得到系统的函数,c语言系统函数(C language system function).doc
  4. redis主从搭建和分片集群搭建
  5. 辽宁师范大学海华学院计算机科学与技术,辽宁师范大学海华学院计算机科学与技术专业综合评价简况表.doc...
  6. MySQL Partition扫盲
  7. python爬取喜马拉雅vip音频安卓_Python爬虫:爬取喜马拉雅音频数据详解
  8. 实用网站合集(持续更新ing)
  9. 年龄的计算方式计算机函数,excel使用时间函数计算年龄 使用Excel函数计算年龄的三种方法...
  10. 熊猫烧香、威金的解决办法
  11. css版权备案等居于页面底部与超出不换行可滑动
  12. 乐观锁实现接口幂等性_calvin-idempotent
  13. vue报错:Not Found - GET https://registry.npmjs.org/- Not found
  14. 进程与程序的区别和联系
  15. 互联网日报 | 6月24日 星期四 | 快手全球月活已达10亿;HarmonyOS 2首批正式版升级;微软市值突破2万亿美元...
  16. Celery启动定时任务遇到报错
  17. 中国金融出版社出版的2013版《风险管理》
  18. Python小爬虫,爬取当前全部股票信息
  19. 大型机学习之初步了解-大型机的现状与前景
  20. ADUM1401ARWZ-RL 亚德诺 数字信号隔离模块

热门文章

  1. 杭电Oj刷题(2017)
  2. scott.emp视图或表不存在,解决scott用户解锁问题
  3. 法国计算机专业学校排名,法国计算机专业大学排名(2020年泰晤士)_快飞留学
  4. 富文本转化为普通文本
  5. JAVA 数字图像处理----非白即黑的灰,2B青年的自画像
  6. Ceph-CephFS部署
  7. Unity真实榴弹炮模拟(真正的大型榴弹炮实现)
  8. HBuildX 打包说明(网站链接打包apk(app))
  9. 编程语言排行榜没有html,TIOBE:2019年12月全球编程语言排行榜
  10. 给孩子积极心理暗示的语句实操