亲测有效!一种完美动态阈值白平衡算法 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了,仍然在创业。也许将来创业成功了,这些博客都能成为励志经历。不成功,那就继续努力。

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

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

时间: 2024-08-25 19:57:37

亲测有效!一种完美动态阈值白平衡算法 Java实现。的相关文章

(完美亲测)可行的安卓手机一键修改型号的教程

安卓手机型号特别多,产品出的也快,手机一下子就成了老机器了,有没有办法可以修改手机的型号,许多做项目的朋友应该也有这样的需求,下面分享一个实用快捷的型号等手机参数修改工具琢石模拟器,这也是朋友分享给我的,我现在亲测可行分享给大家,希望对有需要的朋友有帮助. 琢石模拟器官网上有教程跟手册,只要按照他们教程操作激活就行,激活之后可以方便的一键生成一套完整的手机参数,其中包括imei.imsi.mac.serial.位置经纬度等等100多种参数,当然包括我们需要的手机型号,然后在应用面板中启动应用,应

VCL下最好的皮肤控件AlphaControls 9.05 完美支持XE6 亲测可用

在传统的vcl编程中,因自带的控件效果太过普通,为了更好的用户体验和视觉效果往往会使用第三方的一些皮肤控件,这样的皮肤控件百度上一搜一大把,但往往不了解自己不使用过直接下载下来也是没有用的,因为这要涉及到控件版本 ID工具版本 操作系统 等多方因素配合使用,只有实际测试使用过才可定性. 本文提供的一款控件是经过实际测试过的 测试环境:Windows7 32位 ,Delphi xe6, AlphaControls 9.05 下载链接:http://binstyle.7958.com/down_18

亲测VS2010纯静态编译QT4.8.0,实现VS2010编译调试Qt程序,QtCreator静态发布程序(图文并茂,非常详细)

下载源代码,注意一定是源码压缩包如qt-everywhere-opensource-src-4.8.0.zip,不是Qt发布的已编译的不同版本的标准库如qt-win-opensource-4.8.0-vs2010.exe,这些版本都只是动态编译的,不是我们所需要的.只有用源码包才能做真正的纯静态编译,如果用Qt官网已编译的标准库.exe文件进行编译,有可能出现各种错误,而且编译后的文件巨大,我当时就在这里绕了很大的弯子,请童鞋们注意啦!!可能有些老鸟会笑话,但我本着最严肃的态度告诉菜鸟们,上网查

当今最全面可用的微博分享组件嵌入方法(亲测2019年2月仍有效)

当今最全面可用的微博分享组件嵌入方法(亲测2019年2月仍有效) 最近一直在找一种目前可用的微博分享组件的使用方法,发现有3个大坑: 向网页嵌入微博秀时,需要的uid简单,但需要的verifier值有点难获取,原因在于原生成微博秀页面的 url 及其子链接均会被强制从http重定向跳转到https,此外其response中部分css引用失败导致页面无法完整显示; https页面是没法调用http下的js和css的; 微博秀是需要用iframe来嵌入的,博客园默认不支持iframe标签,可通过构造

Linux 下 将使用Python-Django开发的web应用布置到服务器上(亲测有效)

写在前面: Django是一个卓越的新一代Web框架,相信使用Python的人对此并不陌生,但将我们完成的web应用布置到到服务器上并不是一件容易的事情. Django详细的教程可以参考http://python.usyiyi.cn/django/index.html. Django有自己的一个调试服务器,通过在项目文件夹下执行: python  manage.py runserver 8080(参数8080是设置的端口号,指明该服务器使用端口号为8080) 但是此语句也仅限在自己的机器上进行调

微信现金红包高级红包接口开发亲测

微信现金红包高级红包接口开发时遇到了不少问题,网上搜索发现 微信现金红包高级红包接口开发注意事项 这篇文章问题总结得很完整,里面给的demo是VS高版本写的,运行过程中还是有些参数问题对于首次调试来说有点难度,还有微信支付现金红包接口 的接口介绍,下面的几个问题特别提出: 商户证书,下载的demo需要证书才能正确运行(可调成服务器模拟发送),而证书需要安装到服务器上,demo是控制台程序,需要的证书可直接安装后使用,其中指出的是方法X509Certificate2 Find()中m_Subjec

【原创】Centos下telnet的安装和配置(完全版,Centos5.5亲测)

Centos下telnet的安装和配置(完全版,Centos5.5亲测) 一.查看本机是否有安装telnet(centOS5默认有安装telnet) # rpm -qa |grep telnet 如果显示结果为: telnet-0.17-39.el5 telnet-server-0.17-39.el5 那恭喜你,机器上已经安装了telnet.如果没有安装,请看下一步. 特别说明: telnet分为telnet-client (简称为telnet)和telnet-server.telnet-cli

关于Discuz与jQuery冲突问题的亲测解决方法

最近的一个项目整合dede和discuz程序,客户要求风格统一,所以有很多样式及特效都是要公用的.其中jQuery库定义的函数$()正好与discuz的comme.js中函数一样,这样就冲突了,导致discuz论坛所有js效果失效.网上搜罗了一下,N多的方法,但是很多说的不明不白,我也试了很多方法,浪费了我一个晚上加一个早上,真是费时费力.不过其中一个方法倒是解决了这个冲突,所以共享出来备用,也方便大家. 其实解决方法倒是不难,难的是思路和没有一个详细的步骤,这里我就做个详细的说明.首先思路很简

【Android】Activity生命周期(亲测)

测试手机:Nexus 5   系统:4.4 一.测试 测试代码: 1 package com.example.androidalarm; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.content.res.Configuration; 6 import android.os.Bundle; 7 import android.util.AttributeSet; 8 impo