文章出處
文章列表
幾年沒發文了,重新拿起技術!
最近做圖像處理,要自動處理顏色平衡問題,很多什么直方圖優化之類的,都不完美。所以在博客園找到了這個前輩的文章。
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了,仍然在創業。也許將來創業成功了,這些博客都能成為勵志經歷。不成功,那就繼續努力。
最近正在投身微信公眾號,也小有成就,做了全國最大的樂高公眾號。希望將來有一天能有所成。
感謝各位園友的閱讀,希望這篇文章有幫助!
文章列表
全站熱搜