zoukankan      html  css  js  c++  java
  • 亲测有效!一种完美动态阈值白平衡算法 Java实现。

    几年没发文了,重新拿起技术!

    最近做图像处理,要自动处理颜色平衡问题,很多什么直方图优化之类的,都不完美。所以在博客园找到了这个前辈的文章。

    http://www.cnblogs.com/Imageshop/archive/2013/04/20/3032062.html#commentform

    基于灰度世界、完美反射、动态阈值等图像自动白平衡算法的原理、实现及效果

    很可惜,这篇文章,首先没有源码,其次给出的一些计算过程有问题。所以我直接查看论文原文,以及一些映射公式,现在分享Java实现的版本:

    核心算法:

            BufferedImage img = ImageIO.read(new File("model3.jpg"));
            int pixelsize = img.getWidth() * img.getHeight();
    
            double[][][] YCbCr = new double[img.getWidth()][img.getHeight()][3];
            double Mr = 0, Mb = 0, Ymax = 0;
            for (int i = 0; i < img.getWidth(); i++) {
                for (int j = 0; j < img.getHeight(); j++) {
                    YCbCr[i][j] = toYCbCr(img.getRGB(i, j));
                    Mr += YCbCr[i][j][2];
                    Mb += YCbCr[i][j][1];
                    Ymax = Math.max(Ymax, YCbCr[i][j][0]);
                }
            }
    
            Mr /= pixelsize;
            Mb /= pixelsize;
    
            double Dr = 0, Db = 0;
            for (int i = 0; i < YCbCr.length; i++) {
                for (int j = 0; j < YCbCr[i].length; j++) {
                    Db += Math.abs(YCbCr[i][j][1] - Mb);
                    Dr += Math.abs(YCbCr[i][j][2] - Mr);
                }
            }
            Dr /= pixelsize;
            Db /= pixelsize;
    
            double[][] Y = new double[img.getWidth()][img.getHeight()];
            double[] Yhistogram = new double[256];
            double Ysum = 0;
            for (int i = 0; i < Y.length; i++) {
                for (int j = 0; j < Y[i].length; j++) {
                    int value = (Math.abs(YCbCr[i][j][1] - (Mb + Db * Math.signum(Mb))) < 1.5 * Db) & //
                            (Math.abs(YCbCr[i][j][2]) - (1.5 * Mr + Dr * Math.signum(Mr))) < 1.5 * Dr ? 1 : 0;
                    if (value <= 0)
                        continue;
                    double y = YCbCr[i][j][0];
                    Y[i][j] = y;
                    Yhistogram[(int) Y[i][j]]++;
                    Ysum++;
                }
            }
    
            double Yhistogramsum = 0;
            double Ymin = 0;
            for (int i = Yhistogram.length - 1; i >= 0; i--) {
                Yhistogramsum += Yhistogram[i];
                if (Yhistogramsum > 0.1 * Ysum) {
                    Ymin = i;
                    break;
                }
            }
    
            double Raver = 0, Gaver = 0, Baver = 0;
            double averSum = 0;
            for (int i = 0; i < Y.length; i++) {
                for (int j = 0; j < Y[i].length; j++) {
                    if (Y[i][j] > Ymin) {
    
                        int color = img.getRGB(i, j);
                        int r = (color >> 16) & 0xFF;
                        int g = (color >> 8) & 0xFF;
                        int b = color & 0xFF;
                        Raver += r;
                        Gaver += g;
                        Baver += b;
                        averSum++;
                    }
                }
            }
            Raver /= averSum;
            Gaver /= averSum;
            Baver /= averSum;
    
            double Rgain = Ymax / Raver, Ggain = Ymax / Gaver, Bgain = Ymax / Baver;
            for (int i = 0; i < img.getWidth(); i++) {
                for (int j = 0; j < img.getHeight(); j++) {
                    Color color = new Color(img.getRGB(i, j));
                    int r = ensureColor((int) Math.floor(color.getRed() * Rgain));
                    int g = ensureColor((int) Math.floor(color.getGreen() * Ggain));
                    int b = ensureColor((int) Math.floor(color.getBlue() * Bgain));
                    img.setRGB(i, j, new Color(r, g, b).getRGB());
                }
            }
    
            ImageIO.write(img, "jpg", new File("xxx.jpg"));

    其中计算YCrCb的算法如下:

        // https://mathematica.stackexchange.com/questions/29786/how-to-convert-rgb-to-ycbcr
        private double[] toYCbCr(int color) {
    
            int r = (color >> 16) & 0xFF;
            int g = (color >> 8) & 0xFF;
            int b = color & 0xFF;
    
            double Y = 16 + (65.481 * r / 255 + 128.553 * g / 255 + 24.966 * b / 255);
            double Cb = 128 + (-37.797 * r / 255 - 74.203 * g / 255 + 112 * b / 255);
            double Cr = 128 + (112 * r / 255 - 93.786 * g / 255 - 18.214 * b / 255);
    
            return new double[] { Y, Cb, Cr };
        }

    最后还有一个像素范围检测:

        private int ensureColor(double color) {
            if (color < 0)
                return 0;
            if (color > 255)
                return 255;
            return (int) color;
        }

    实际效果:

     
       

    效果非常好。我也看了下原作者的问题,应该是计算YCrCb出错了。

     本次分享完毕啦!好几年没有在博客园发文了,说下近况了。第一次进博客园是10多年前,在上海交大读研究生的一个穷小孩。研究生毕业之后一直磕磕碰碰在创业,到了现在36了,仍然在创业。也许将来创业成功了,这些博客都能成为励志经历。不成功,那就继续努力。

    最近正在投身微信公众号,也小有成就,做了全国最大的乐高公众号。希望将来有一天能有所成。

    感谢各位园友的阅读,希望这篇文章有帮助!

  • 相关阅读:
    一些遇到的错误的总结:
    ThinkPHP add save delete的返回说明
    一些实用函数 :去除html标签//去除空白//截取汉字
    group_concat
    linux环境下使用mkdir()函数无法创建目录的问题
    报错:Namespace declaration statement has to be the very first statement in the script的解决方法
    ThinkPHP中,字段更新加1的几种写法
    小狼毫输入法安装与简单配置(windows系统)
    对win10和win11的吐嘈
    看死亡诗社时有的一点新想法
  • 原文地址:https://www.cnblogs.com/zc22/p/7101930.html
Copyright © 2011-2022 走看看