您的位置:首页 > 编程语言 > Java开发

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值生成一套不错的配色方案

先看看效果图:



大概的流程是这样的:

定义了以下几个内部类

//定义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 算法 图片