惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

有赞技术团队
有赞技术团队
量子位
B
Blog RSS Feed
Schneier on Security
Schneier on Security
L
LINUX DO - 最新话题
博客园 - 三生石上(FineUI控件)
Recent Announcements
Recent Announcements
Hacker News: Ask HN
Hacker News: Ask HN
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google DeepMind News
Google DeepMind News
N
News | PayPal Newsroom
阮一峰的网络日志
阮一峰的网络日志
Microsoft Security Blog
Microsoft Security Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tailwind CSS Blog
MongoDB | Blog
MongoDB | Blog
大猫的无限游戏
大猫的无限游戏
PCI Perspectives
PCI Perspectives
aimingoo的专栏
aimingoo的专栏
D
Docker
T
The Exploit Database - CXSecurity.com
Last Week in AI
Last Week in AI
W
WeLiveSecurity
Stack Overflow Blog
Stack Overflow Blog
月光博客
月光博客
Vercel News
Vercel News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
J
Java Code Geeks
O
OpenAI News
C
Cisco Blogs
Hacker News - Newest:
Hacker News - Newest: "LLM"
爱范儿
爱范儿
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Threat Research - Cisco Blogs
Cisco Talos Blog
Cisco Talos Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Help Net Security
Help Net Security
Scott Helme
Scott Helme
The Hacker News
The Hacker News
Y
Y Combinator Blog
A
Arctic Wolf
V
V2EX
P
Proofpoint News Feed
Simon Willison's Weblog
Simon Willison's Weblog
A
About on SuperTechFans
S
Securelist
G
Google Developers Blog
Cyberwarzone
Cyberwarzone
The GitHub Blog
The GitHub Blog

博客园 - 辰

【破解】webix 9.1.6 试用版 弹窗提示破解教程 【经验】微信小程序 canvas 图片拉伸 对 wx.canvasToTempFilePath 截图导出的影响。 【经验】微信小程序Canvas 的画图尺寸学习研究。 [入门到吐槽系列] 微信小程序 上传图片 前端压缩 保存到云存储 使用Canvas新API的巨坑! [入门到吐槽系列] 微信小程序 敏感违规图片检测 mediaCheckAsync,客服接口 消息推送 的各种坑分享! [突发救援] 网站突发被腾讯云因未备案 屏蔽,60分钟内抢救恢复成功! [入门到吐槽系列] Webix 10分钟入门 二 表单Form的使用 [入门到吐槽系列] Webix 10分钟入门 一 管理后台制作 租了一台华为云耀云服务器,却直接被封公网ip,而且是官方的bug导致! 从事游戏行业也有10年,让我来说下游戏的本质,为什么上瘾。 Egret 生成 自带EUI 的微信小游戏 踩坑! JMeter入门 像屎一样的 Spring Boot入门,总算有反应了 Windows 2008 - 由于管理员设置的策略,该磁盘处于脱机状态 egret调用页面js的方法。 egret GUI 和 egret Wing 是我看到h5 最渣的设计 Egret 集成第三方库 记录 乐高情报站7月份抽奖数据汇总。 今天是第二个最值得纪念的日子。
亲测有效!一种完美动态阈值白平衡算法 Java实现。
· 2017-07-01 · via 博客园 - 辰

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

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

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了,仍然在创业。也许将来创业成功了,这些博客都能成为励志经历。不成功,那就继续努力。

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

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