首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 平面设计 > 图形图像 >

[Java]图像色彩匹配算法的实施

2012-08-31 
[Java]图像颜色匹配算法的实施在问答频道中看到这个问题:要分析的图片如下所示:于是设计了算法:0. 建立一

[Java]图像颜色匹配算法的实施
在问答频道中看到这个问题:



要分析的图片如下所示:



于是设计了算法:

0. 建立一个颜色分区数组为a,内包含颜色区间值v
1. 建立一个颜色统计MultiValuedMap称为m:key -> v; value -> [x, y] x, y为图片中像素坐标
2. 读取图片f
3. 读取f中每一个像素的hsl
4. 计算这个像素的hsl值与a中所有已有颜色区间v的偏差d
* 如果d在允许的范围s内,则将此值计入到m
* 如果d大于s,在a中建立新的v,并在m中新添此坐标

a. 这个算法中,s将决定颜色的相似度,s越小,颜色划分的越细
b. 最后的结果将从m中出,每一个key所包含的value数量,将成为颜色的百分比计算依据

在上面的算法中,需要用到一些基础工具,比如MultiValuedMap和HSLColor。

其中MultiValuedMap可以支持我们使用一个KEY保存多个值。因为我们要匹配三种颜色,即:红,绿,蓝。所有匹配到的图片中的颜色,要把它们的坐标计入到三种颜色中的一个。因此,颜色就是KEY,坐标就是VALUE。

下面是MultiValuedMap的代码:

import java.util.*;public class MultiValueMap<K, V> {    private Map<K, Collection<V>> map;    private Class<? extends Collection<V>> clazz;    public MultiValueMap(Collection<V> coll) {        map = new HashMap<K, Collection<V>>();        this.clazz = (Class<? extends Collection<V>>) coll.getClass();    }    public void addValue(K key, V value) {        Collection<V> collection = map.get(key);        if (collection == null) {            collection = createCollection();            if (collection == null) return;            map.put(key, collection);        }        collection.add(value);    }    public Set<K> keys() {        return map.keySet();    }    public Collection<V> getValues(K key) {        Collection<V> collection = map.get(key);        if (collection == null)            return Collections.emptySet();        return collection;    }    private Collection<V> createCollection() {        Collection<V> collection = null;        try {            collection = clazz.newInstance();        } catch (InstantiationException ex) {            // handling here        } catch (IllegalAccessException ex) {            // handling here        }        return collection;    }}



然后,我们在算法中要用到HSL颜色空间,Java对图像的操作中,默认使用RGB空间颜色,因此需要进行转换,我们使用HSLColor进行颜色的转换。这个类的源代码在这里:

http://code.google.com/p/w3m-informio/source/browse/trunk/src/org/w3mv2/miniutils/HSLColor.java

接下来是算法实现代码:

import javax.imageio.ImageIO;import javax.swing.*;import java.awt.*;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;import java.util.ArrayList;import java.util.List;public class Picker {    // Hue偏差允许0.5%    private static final double Hd = 0.5;    // Saturation偏差允许0.5%    private static final double Sd = 0.5;    // Luminance编差允许1%    private static final double Ld = 1.0;    // 图中的绿色样本    private static final Color GREEN = new Color(46, 189, 102);    // 将绿色转化到hsl空间    private static final HSLColor GREEN_HSL = new HSLColor(GREEN);    // 图中的蓝色样本    private static final Color BLUE = new Color(87, 91, 212);    // 将蓝色转化到hsl空间    private static final HSLColor BLUE_HSL = new HSLColor(BLUE);    // 图中的红色样本    private static final Color RED = new Color(238, 48, 50);    // 将红色转化到hsl空间    private static final HSLColor RED_HSL = new HSLColor(RED);    private static final HSLColor[] COLOR_SAMPLES = {GREEN_HSL, BLUE_HSL, RED_HSL};    // 允许的颜色偏差范围为1.5%    private static final double SCOPE = 0.15;    public static void main(String[] args) throws IOException {        // 读取图片文件        File file = new File(Picker.class.getClassLoader().getResource("").getPath() + "sample.jpg");        BufferedImage bufImg = ImageIO.read(file);        int height = bufImg.getHeight();        int width = bufImg.getWidth();                MultiValueMap<Color, Point> mvm =                new MultiValueMap<Color, Point>(new ArrayList<Point>());        // 总像素数        int totalPixels = width * height;        for (int i = 0; i < width; i++) {            for (int j = 0; j < height; j++) {                // 获取当前像素的颜色                Color rgbColor = new Color(bufImg.getRGB(i, j));                // 去掉图中所有的白底                if (rgbColor.equals(Color.WHITE)) {                    totalPixels -= 1;                    continue;                }                // 转化到HSL空间                HSLColor color = new HSLColor(rgbColor);                // 计算颜色偏差                for (HSLColor sampleColor : COLOR_SAMPLES) {                    double hDeviation = Math.abs(color.getHue() - sampleColor.getHue());                    double sDeviation = Math.abs(color.getSaturation() - sampleColor.getSaturation());                    double lDeviation = Math.abs(color.getLuminance() - sampleColor.getLuminance());                    double result = Math.sqrt(Hd * hDeviation * hDeviation + Sd                            * sDeviation * sDeviation + Ld                            * lDeviation * lDeviation) / 100;                    if (result < SCOPE) {                        // 颜色与红,绿,蓝中的一种匹配上了,加入到MultiValueMap当中                        mvm.addValue(sampleColor.getRGB(), new Point(i, j));                        // 颜色匹配上了,没有必要再计算别的颜色                        break;                    }                }            }        }        System.out.println("Green Color: " + ((double) mvm.getValues(GREEN).size() / (double) totalPixels * 100) + "%");        System.out.println("Red Color: " + ((double) mvm.getValues(RED).size() / (double) totalPixels * 100) + "%");        System.out.println("Blue Color: " + ((double) mvm.getValues(BLUE).size() / (double) totalPixels * 100) + "%");    }}


运行上述代码结果如下:

Green Color: 8.915814359870483%Red Color: 3.0156241969471145%Blue Color: 13.816364290486716%


如果想直观地看到颜色的统计结果,可以把抓到的坐标点画出来:

import javax.swing.*;import java.awt.*;public class Points extends JPanel {    private MultiValueMap<Color, Point> pixels;    public Points(MultiValueMap<Color, Point> pixels) {        this.pixels = pixels;    }    public void paintComponent(Graphics g) {        super.paintComponent(g);        Graphics2D g2d = (Graphics2D) g;        for (Color color : pixels.keys()) {            g2d.setColor(color);            for (Point p : pixels.getValues(color)) {                g2d.drawLine(p.x, p.y, p.x, p.y);            }        }    }}


然后在Picker.java中末尾加上一段:

JFrame frame = new JFrame("Result");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.add(new Points(mvm));frame.setSize(width, height);frame.setLocationRelativeTo(null);frame.setVisible(true);


输出结果如下:



可以直观地看到统计结果是准确的。可以试试看将Picker.java中的SCOPE值调成不一样的时候,统计结果的变化。

我将文中用到的源代码放在了这里:

https://github.com/liweinan/image-detect



有兴趣可以迁出:

git clone https://github.com/liweinan/image-detect.git


然后运行:

mvn install


mvn exec:java -Dexec.mainClass="net.bluedash.Picker"



参考资料

http://en.wikipedia.org/wiki/HSL_and_HSV

http://stackoverflow.com/questions/1678457/best-algorithm-for-matching-colours/1678497#1678497

http://www.javalobby.org/forums/thread.jspa?threadID=17048

http://zetcode.com/tutorials/java2dtutorial/basicdrawing/

http://stackoverflow.com/questions/3153337/how-do-i-get-my-current-working-directory-in-java

热点排行