java实现:根据图片生成配色方案
2016-03-30 01:01
1111 查看
因为项目的需求,要做一个根据图片自动生成配色方案的功能。
网上查了文献大概有几种算法
1.K-means聚类算法
2.八叉树算法
3.最小差值法
4….
这里我选用了K-means算法,就不介绍K-means算法了。网上很多参考资料
先来介绍一下算法的思路:
将每个像素点的RGB值映射到xyz三维坐标中,这样相当于一张图片所有的像素点都分散在三维坐标当中
在图片上随机取N个点作为种子点,然后计算出图片上的所有像素点与这N个种子点的距离,每个像素点可以得到与其距离最近的种子点,就可以把该像素点加入到该种子点的点群中(这里体现了聚类的思想)。
遍历过整张图片后,每个像素都会加入到其中一个种子点群中(种子点群会记录下他拥有的全部像素点RGB总和,拥有的像素点个数),然后每个种子点群求得自己所拥有点的平均RGB值,作为新的种子点代替原来的种子结点。
重复这个过程,直到种子结点不再移动(收敛于某个RGB值)或者迭代次数超过阈值,最后得到的权重(点群中 点的数量)最高的RGB值便是图片的主色调, 也可以搭配权重前3的RGB值生成一套不错的配色方案
先看看效果图:
大概的流程是这样的:
定义了以下几个内部类
1.按照比例获取图片上一定数量的点,筛选其中一些点作为种子点
2.主循环
isEnd()方法:
最后附上源码(imageUtil.java):
网上查了文献大概有几种算法
1.K-means聚类算法
2.八叉树算法
3.最小差值法
4….
这里我选用了K-means算法,就不介绍K-means算法了。网上很多参考资料
先来介绍一下算法的思路:
将每个像素点的RGB值映射到xyz三维坐标中,这样相当于一张图片所有的像素点都分散在三维坐标当中
在图片上随机取N个点作为种子点,然后计算出图片上的所有像素点与这N个种子点的距离,每个像素点可以得到与其距离最近的种子点,就可以把该像素点加入到该种子点的点群中(这里体现了聚类的思想)。
遍历过整张图片后,每个像素都会加入到其中一个种子点群中(种子点群会记录下他拥有的全部像素点RGB总和,拥有的像素点个数),然后每个种子点群求得自己所拥有点的平均RGB值,作为新的种子点代替原来的种子结点。
重复这个过程,直到种子结点不再移动(收敛于某个RGB值)或者迭代次数超过阈值,最后得到的权重(点群中 点的数量)最高的RGB值便是图片的主色调, 也可以搭配权重前3的RGB值生成一套不错的配色方案
先看看效果图:
大概的流程是这样的:
定义了以下几个内部类
//定义RGB像素点 public class Point{ int R; int G; int B; //..省略一些构造方法 //计算两个RGB值的相似度 (根据方差) public int colorDistance(Point point){ int absR = this.R - point.R; int absG = this.G - point.G; int absB = this.B - point.B; return (int) Math.sqrt(absR*absR+absG*absG+absB*absB); } //..省略了一些重写的equls方法等 //种子点群内部类 public class PointGroup{ int sumaryR = 0; int sumaryG = 0; int sumaryB = 0; int pointCount = 0; Point point; public void addPoint(Point point){ this.sumaryR += point.R; this.sumaryG += point.G; this.sumaryB += point.B; pointCount++; } public Point getNewRGB(){ Point newpoint = new Point(); if(pointCount != 0 ){ newpoint.R = sumaryR / pointCount; newpoint.G = sumaryG / pointCount; newpoint.B = sumaryB / pointCount; }else{ newpoint= this.point; } return newpoint; } }
1.按照比例获取图片上一定数量的点,筛选其中一些点作为种子点
//得到图上上32*32个点放入集合中作为种子点群的备选点 List<PointGroup> rootPoint = new ArrayList<PointGroup>(); for(int i = 0; i < width; i += offsetWidth) for(int j = 0; j < height; j += offsetHeight){ PointGroup pg = new PointGroup(); //得到图片上坐标为(i,j)的RGB值 pg.point = getRGB(i,j); } //设置相似颜色初始阈值 int threshold = 6; //当前的种子点个数大于用户设置的上限时 提高阈值 以减少点的数量 while(rootPoint.size() > rootPointNum){ for(int i = 0; i < rootPoint.size(); i++) for(int j = i+1; j < rootPoint.size(); j++){ if(rootPoint.get(i).point.colorDistance(rootPoint.get(j).point) < threshold){ rootPoint.remove(j); } } //提高阈值 threshold += 1; }
2.主循环
//统计运算数次,当超过阈值时退出循环 int count = 0; do{ //遍历图片所有像素点,将每个点都加入与其最接近的种子点 for(int i = 0; i < width ; i++) for(int j = 0; j < height; j++){ Point point = getRGB(i,j); int index = 0; int dis = 10000; for (int m = 0; m < rootPoint.size() ; m++) { //计算当前点与第m个种子点的距离 int curDis = point.colorDistance(rootPoint.get(m).point); //如果距离小于最小距离 ,则替换最小距离并记录种子结点下标index if(curDis < dis){ dis = curDis; index = m; } } //得到最近的下标为index的种子结点,将该点加入种子结点 rootPoint.get(index).addPoint(point); count++;//运算次数加1 } //将得到的种子点群进行运算得到新的种子点群,判断种子点群是否发生变化。如果没有则继续统计 if(isEnd(rootPoint)){ break; } }while(count < maxIterater);
isEnd()方法:
public boolean isEnd(List<PointGroup> rootPoint){ Point oldpoint = new Point();//原本的种子点群 Point newpoint = new Point();//新的种子点群 for (PointGroup pointGroup : rootPoint) { oldpoint.add(pointGroup.point); newpoint.add(pointGroup.getNewRGB()); //将新的种子点群代替原本的种子点群 pointGroup.point = pointGroup.getNewRGB(); } //如果新旧相同代表种子点群收敛完毕,循环结束 if(oldpoint.equals(newpoint)){ return true; }else{ return false; } }
最后附上源码(imageUtil.java):
package org.daxiang.imageUtil; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Random; import javax.imageio.ImageIO; import org.daxiang.imageUtil.ImageUtil.Point; public class ImageUtil { BufferedImage image = null; //定义RGB像素点 public class Point{ int R; int G; int B; Point(){} public Point(int r, int g, int b) { super(); R = r; G = g; B = b; } public int getR() { return R; } public void setR(int r) { R = r; } public int getG() { return G; } public void setG(int g) { G = g; } public int getB() { return B; } public void setB(int b) { B = b; } public int colorDistance(Point point){ int absR = this.R - point.R; int absG = this.G - point.G; int absB = this.B - point.B; return (int) Math.sqrt(absR*absR+absG*absG+absB*absB); } @Override public int hashCode() { return super.hashCode(); }; @Override public boolean equals(Object obj) { boolean result = false; Point point = (Point)obj; if(this.R==point.R && this.G==point.G && this.B==point.B){ result = true; } return result; } public void add(Point point){ this.R += point.R; this.G += point.G; this.B += point.B; } @Override public String toString() { // TODO Auto-generated method stub return "R="+this.R+" G="+this.G+" B="+this.B; } } //点群内部类 public class PointGroup{ int sumaryR = 0; int sumaryG = 0; int sumaryB = 0; int pointCount = 0; Point point; public void addPoint(Point point){ this.sumaryR += point.R; this.sumaryG += point.G; this.sumaryB += point.B; pointCount++; } public Point getNewRGB(){ Point newpoint = new Point(); if(pointCount != 0 ){ newpoint.R = sumaryR / pointCount; newpoint.G = sumaryG / pointCount; newpoint.B = sumaryB / pointCount; }else{ newpoint= this.point; } return newpoint; } } class pointCampare implements Comparator{ @Override public int compare(Object o1, Object o2) { PointGroup p1 = (PointGroup)o1; PointGroup p2 = (PointGroup)o2; if((p1.point.R+p1.point.G+p1.point.B) >= (p2.point.R+p2.point.G+p2.point.B)){ return 1; }else{ return 0; } } } public int max(int a, int b, int c){ int result = 0; if(a >= b && a >= c){ result = 1; }else if(b >= a && b >= c){ result = 2; }else if(c >= b && c >= a){ result = 3; } return result; } //得到某位置的像素RBG public Point getRGB(int x, int y){ Point point = new Point(); int pixel = image.getRGB(x, y); point.R = (pixel & 0xff0000) >> 16; point.G = (pixel & 0xff00) >> 8; point.B = (pixel & 0xff); return point; } public boolean isEnd(List<PointGroup> rootPoint){ Point oldpoint = new Point(); Point newpoint = new Point(); for (PointGroup pointGroup : rootPoint) { oldpoint.add(pointGroup.point); newpoint.add(pointGroup.getNewRGB()); pointGroup.point = pointGroup.getNewRGB(); } if(oldpoint.equals(newpoint)){ return true; }else{ return false; } } public List<Point> getThreeFamilly(List<PointGroup> rootPoint){ List<Point> pointList = new ArrayList<Point>(); //去除与白色相近的颜色 Iterator<PointGroup> it = rootPoint.iterator(); while(it.hasNext()){ PointGroup i = it.next(); if(i.point.colorDistance(new Point(255,255,255)) < 30){ it.remove(); } } //根据RGB的总和对颜色排序 Collections.sort(rootPoint,new pointCampare()); //如果颜色小于等于3个,直接输出 if(rootPoint.size() <= 3){ for(int i = 0 ; i < rootPoint.size(); i++){ pointList.add(rootPoint.get(i).point); } return pointList; } int index = 1; int min = 100000; //得到3个最相近的颜色 for(int i = 1 ; i < rootPoint.size() - 1; i++){ int temp = rootPoint.get(i+1).point.colorDistance(rootPoint.get(i).point) + rootPoint.get(i).point.colorDistance(rootPoint.get(i-1).point); if(temp < min){ min = temp; index = i; } } //根据R-G-B中最大的排序 int max = 0; //得到RGB中最大的分量 if(rootPoint.get(index).point.R >= rootPoint.get(index).point.G && rootPoint.get(index).point.R >= rootPoint.get(index).point.B){ max = 1; }else if(rootPoint.get(index).point.G >= rootPoint.get(index).point.R && rootPoint.get(index).point.G >= rootPoint.get(index).point.B){ max = 2; }else{ max = 3; } switch (max) { //根据R值排序 case 1: int temp = max(rootPoint.get(index-1).point.R,rootPoint.get(index).point.R,rootPoint.get(index+1).point.R); pointList.add(rootPoint.get(index - 2 + temp).point); if(temp == 1){ if(rootPoint.get(index).point.R > rootPoint.get(index + 1).point.R){ pointList.add(rootPoint.get(index).point); pointList.add(rootPoint.get(index + 1).point); }else{ pointList.add(rootPoint.get(index + 1).point); pointList.add(rootPoint.get(index).point); } } if(temp == 2){ if(rootPoint.get(index - 1).point.R > rootPoint.get(index + 1).point.R){ pointList.add(rootPoint.get(index - 1).point) ; pointList.add(rootPoint.get(index + 1).point) ; }else{ pointList.add(rootPoint.get(index + 1).point) ; pointList.add(rootPoint.get(index - 1).point) ; } } if(temp == 3){ if(rootPoint.get(index - 1).point.R > rootPoint.get(index).point.R){ pointList.add(rootPoint.get(index - 1).point) ; pointList.add(rootPoint.get(index).point) ; }else{ pointList.add(rootPoint.get(index).point) ; pointList.add(rootPoint.get(index - 1).point) ; } } break; //根据G值排序 case 2: int temp1 = max(rootPoint.get(index-1).point.G,rootPoint.get(index).point.G,rootPoint.get(index+1).point.G); pointList.add(rootPoint.get(index - 2 + temp1).point); if(temp1 == 1){ if(rootPoint.get(index).point.R > rootPoint.get(index + 1).point.R){ pointList.add(rootPoint.get(index).point); pointList.add(rootPoint.get(index + 1).point); }else{ pointList.add(rootPoint.get(index + 1).point); pointList.add(rootPoint.get(index).point); } } if(temp1 == 2){ if(rootPoint.get(index - 1).point.R > rootPoint.get(index + 1).point.R){ pointList.add(rootPoint.get(index - 1).point) ; pointList.add(rootPoint.get(index + 1).point) ; }else{ pointList.add(rootPoint.get(index + 1).point) ; pointList.add(rootPoint.get(index - 1).point) ; } } if(temp1 == 3){ if(rootPoint.get(index - 1).point.R > rootPoint.get(index).point.R){ pointList.add(rootPoint.get(index - 1).point) ; pointList.add(rootPoint.get(index).point) ; }else{ pointList.add(rootPoint.get(index).point) ; pointList.add(rootPoint.get(index - 1).point) ; } } break; //根据B值排序 case 3: int temp3 = max(rootPoint.get(index-1).point.B,rootPoint.get(index).point.B,rootPoint.get(index+1).point.B); pointList.add(rootPoint.get(index - 2 + temp3).point); if(temp3 == 1){ if(rootPoint.get(index).point.R > rootPoint.get(index + 1).point.R){ pointList.add(rootPoint.get(index).point); pointList.add(rootPoint.get(index + 1).point); }else{ pointList.add(rootPoint.get(index + 1).point); pointList.add(rootPoint.get(index).point); } } if(temp3 == 2){ if(rootPoint.get(index - 1).point.R > rootPoint.get(index + 1).point.R){ pointList.add(rootPoint.get(index - 1).point) ; pointList.add(rootPoint.get(index + 1).point) ; }else{ pointList.add(rootPoint.get(index + 1).point) ; pointList.add(rootPoint.get(index - 1).point) ; } } if(temp3 == 3){ if(rootPoint.get(index - 1).point.R > rootPoint.get(index).point.R){ pointList.add(rootPoint.get(index - 1).point) ; pointList.add(rootPoint.get(index).point) ; }else{ pointList.add(rootPoint.get(index).point) ; pointList.add(rootPoint.get(index - 1).point) ; } } break; default: break; } return pointList; } /** * 根据图片得到配色方案 * @param image bufferImage * @param rootPointNum 种子点的个数 * @param colorNum 得到的色调个数 * @return */ public List<String> getColorSolution(BufferedImage image, int rootPointNum, int colorNum){ this.image = image; int width = image.getWidth(); int height = image.getHeight(); //定义最大迭代次数 int maxIterater = 5000000; int offsetWidth = (int)(width / 32); int offsetHeight = (int)(height / 32); //随机得到32*32个点,在其中取rootPointNum个点,取RGB值做为种子点 List<PointGroup> rootPoint = new ArrayList<PointGroup>(); for(int i = 0; i < width; i += offsetWidth) for(int j = 0; j < height; j += offsetHeight){ PointGroup pg = new PointGroup(); pg.point = getRGB(i,j); rootPoint.add(pg); } //设置种子点集群初始阈值 int threshold = 6; //当当前的种子点个数大于用户设置的上限时 提高阈值 while(rootPoint.size() > rootPointNum){ for(int i = 0; i < rootPoint.size(); i++) for(int j = i+1; j < rootPoint.size(); j++){ if(rootPoint.get(i).point.colorDistance(rootPoint.get(j).point) < threshold){ rootPoint.remove(j); } } threshold += 1; } // for (Point point : rootPoint) { // System.out.println("R="+point.R+" G="+point.G+" B="+point.B); // } int count = 0; do{ //遍历图片所有像素点,将每个点都加入其中一个种子点 for(int i = 0; i < width ; i++) for(int j = 0; j < height; j++){ Point point = getRGB(i,j); int index = 0; int dis = 10000; for (int m = 0; m < rootPoint.size() ; m++) { int curDis = point.colorDistance(rootPoint.get(m).point); if(curDis < dis){ dis = curDis; index = m; } } rootPoint.get(index).addPoint(point); count++; } //查看是新的种子点平均RGB是否等于原来的种子点,如果有,则说明收敛完毕 if(isEnd(rootPoint)){ break; } }while(count < maxIterater); List<Point> pointList = getThreeFamilly(rootPoint); for(int i = 0; i < pointList.size(); i++){ System.out.println(pointList.get(i).toString()); } return null; } }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树