java图像处理之自动对比度
? ? ? ??在圖像處理軟件Photoshop中有一個(gè)自動(dòng)對(duì)比度功能,可以一鍵調(diào)節(jié)圖像亮度、對(duì)比度。比如,像下面這張由于曝光不足形成的非常暗的圖像,只有圖像中間比較亮一些的路面才能看到,其他地面上的物體幾乎無(wú)法看見。
? ? ? ??那么再來(lái)看一下使用Photoshop自動(dòng)對(duì)比度之后效果。
? ? ? ? 一鍵操作后,圖像上的大部分物體都體現(xiàn)出來(lái)了,調(diào)整后的亮度、對(duì)比度、飽和度等幾乎不會(huì)給人失真的感覺,效果比手動(dòng)調(diào)節(jié)亮度、對(duì)比度、或者曲線調(diào)整要好很多。實(shí)際上,這個(gè)功能的牛逼之處不止如此,再看另一張圖。
? ? ? ??這是一張受到霧霾干擾的圖像,整體感覺就是非常朦朧,看不清地面物體。用自動(dòng)對(duì)比度看看效果:
? ? ? ? 去霧效果杠杠的,完全就像是沒有霧霾的天氣拍出來(lái)的。 那么如此牛逼的功能是怎么實(shí)現(xiàn)的呢?其實(shí)原理還是比較簡(jiǎn)單,一般圖像由RGB三個(gè)通道合成,每個(gè)通道由0~255數(shù)值來(lái)體現(xiàn),如果是比較暗的圖像,其RGB通道數(shù)值可能在比較低的范圍內(nèi),比如第一張非常暗的圖像,其RGB三個(gè)通道數(shù)值范圍為(0~60),即最亮的地方才60的亮度值,離最大亮度255還差的遠(yuǎn)。 ? ?
? ? ? ??有了圖像的各個(gè)通道數(shù)據(jù),就可以在此基礎(chǔ)上做些文章了,比如,可以直接將(0~60)映射到(0~255)。使用java編寫代碼測(cè)試一下,用ImageIO讀取和寫入圖像,用bufferedImage對(duì)象獲取圖像RGB信息,然后對(duì)各個(gè)通道值進(jìn)行處理。
代碼如下:
// 自動(dòng)對(duì)比度 public void autoContrast(BufferedImage image) {int width = image.getWidth();int height = image.getHeight();for(int i = 0; i < width; i++) {for(int j = 0; j < height; j++) {int rgb = image.getRGB(i, j);double r = (rgb >> 16) & 0xff;double g = (rgb >> 8) & 0xff;double b = rgb & 0xff;//進(jìn)行轉(zhuǎn)換r = getContrastByMaxMin(r);g = getContrastByMaxMin(g);b = getContrastByMaxMin(b);rgb = (255 & 0xff) << 24 | (clamp((int)r) & 0xff) << 16 | (clamp((int)g) & 0xff) << 8 | (clamp((int)b) & 0xff);image.setRGB(i, j, rgb);}} } //根據(jù)亮度最大最小值拉伸對(duì)比度 private double getContrastByMaxMin(double a) {if (a >= arr[0] && a <= arr[1]) {result = (a - arr[0]) * 255 / (arr[1] - arr[0]);}return result; }??運(yùn)行效果如下:
? ? ? ??圖像確實(shí)變亮了!但是仔細(xì)看看,效果似乎比Photoshop差一些,這是怎么回事?
? ? ? ??其實(shí),雖然獲取到了各個(gè)通道的動(dòng)態(tài)范圍(0~60),但這只是最大最小值,而大部分?jǐn)?shù)值范圍可能在(10~50)之間,那么在進(jìn)行拉伸的時(shí)候,很多值并沒有拉伸到合適的數(shù)值上。那么Photoshop是怎么處理的呢?很簡(jiǎn)單!就是對(duì)上下范圍進(jìn)行裁切,Photoshop自動(dòng)對(duì)比度是裁切掉上下范圍各0.001%的像素,再得到需要進(jìn)行映射的范圍,對(duì)范圍以外的數(shù)值直接映射到0或255。當(dāng)然,要獲得準(zhǔn)確的裁切值,需要對(duì)各個(gè)通道值進(jìn)行統(tǒng)計(jì),即統(tǒng)計(jì)圖像各個(gè)通道在(0~255)上分布概率。
代碼如下:
// 自動(dòng)對(duì)比度 public void autoContrast(BufferedImage image) {int width = image.getWidth();int height = image.getHeight();//計(jì)算圖像強(qiáng)度分布頻率Map<Integer,Double> map = getPDF(image);//獲取最大最小強(qiáng)度int min = getMin(map);int max = getMax(map);int[] arr = new int[] {min,max};//進(jìn)行對(duì)比度拉伸for(int i = 0; i < width; i++) {for(int j = 0; j < height; j++) {int rgb = image.getRGB(i, j);double r = (rgb >> 16) & 0xff;double g = (rgb >> 8) & 0xff;double b = rgb & 0xff;//進(jìn)行轉(zhuǎn)換r = getContrastByMaxMin(r, arr);g = getContrastByMaxMin(g, arr);b = getContrastByMaxMin(b, arr);rgb = (255 & 0xff) << 24 | (clamp((int)r) & 0xff) << 16 | (clamp((int)g) & 0xff) << 8 | (clamp((int)b) & 0xff);image.setRGB(i, j, rgb);}} }//取出最小強(qiáng)度 private int getMin(Map<Integer, Double> map){double temp = 0.0;int result = 0;for(int i = 0; i < 256; i++) {double num = map.get(i);temp += num;if (temp >= 0.001) {result = i;break;}}return result; }//取出最大強(qiáng)度 private int getMax(Map<Integer, Double> map){double temp = 0.0;int result = 0;for(int i = 255; i >= 0; i--) {double num = map.get(i);temp += num;if (temp >= 0.001) {result = i;break;}}return result; }//根據(jù)亮度最大最小值拉伸對(duì)比度 private double getContrastByMaxMin(double a, int[] arr) {double result = 0.0;if (arr[0] == arr[1]) {result = arr[0];}if (a > arr[1]) {result = 255;}if (a < arr[0]) {result = 0;}if (a >= arr[0] && a <= arr[1]) {result = (a - arr[0]) * 255 / (arr[1] - arr[0]);}return result; }/** * 統(tǒng)計(jì)圖像從0~255之間分布 * * @return */ private Map<Integer, Double> getPDF(BufferedImage image) {Map<Integer, Double> map = new HashMap<>();int width = image.getWidth();int height = image.getHeight();double totalPixel = width * height;for (int i = 0; i < 256; i++) {map.put(i, 0.0);// 通過循環(huán),往集合里面填充0~255個(gè)位置,初始值都為0 }//分別統(tǒng)計(jì)圖像上0~255上分布總數(shù)for (int i = 0; i < width; i++) {for (int j = 0; j < height; j++) {int rgb = image.getRGB(i, j);int r = (rgb >> 16) & 0xff;int g = (rgb >> 8) & 0xff;int b = rgb & 0xff;map.put(r, map.get(r) + 1);map.put(g, map.get(g) + 1);map.put(b, map.get(b) + 1);}}//計(jì)算分布概率for(int i = 0; i < 256; i++) {double value = map.get(i);value = value / (3 * totalPixel);map.put(i, value);}return map; } // 判斷a,r,g,b值,大于256返回256,小于0則返回0,0到256之間則直接返回原始值 private int clamp(int rgb) {if (rgb > 255)return 255;if (rgb < 0)return 0;return rgb; }? ? ? ? 再看效果:
? ? ? ? 是不是跟Photoshop一毛一樣!
?
?
?
總結(jié)
以上是生活随笔為你收集整理的java图像处理之自动对比度的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java图像处理,拷贝图像EXIF信息
- 下一篇: 用java实现经纬度坐标度分秒与度批量转