java根據圖片中綠色像素點的多少進行排序
一個朋友被綠了,看見綠色就會很傷感,作為好兄弟的我當然看不下去,感覺有我必要也必須做一點什么。正好我又在寫java程序,那就寫一個小程序幫他把電腦里的圖片排一下順序,根據綠色的程度進行排序,最后把排好序的圖片偷偷的放到他的電腦里去。為了好兄弟做這么多的事情,正所謂:事了拂衣去,功藏身與名啊!(當然是倒序排的)
一、利用for循環讀取圖片相信學過圖像處理的小伙伴都知道,一張圖片由很多像素組成(當然,矢量圖片除外,大家下來可以了解為啥矢量圖片不是由像素點組成的)。因此,不管是什么圖片我們都看作是一個平面,因為就可以用坐標的方式去讀取圖片啦!那就廢話不多說直接開始!
二、代碼的邏輯1.先給大家看看主方法里面都有一些什么內容public static void main(String[] args) {HashMap<File, Object> imageMap = new HashMap<File, Object>();//用hashMap將文件和對應的像素點數量裝到一起File fileDir = new File('D:DownloadTestFile');//將要進行排序的文件目錄File[] listFiles = fileDir.listFiles();for (File readFile : listFiles) {getImagePixel(readFile, imageMap);//獲取圖片的綠色像素點數量的多少}HashMapSortUtils hashMapSortUtils = new HashMapSortUtils(imageMap, 1, 3, 'Mus');LinkedHashMap<File,Object> sortFileMap = hashMapSortUtils.sortFileMap();//將圖片按照像素點的數量進行排序hashMapSortUtils.renameFiles(sortFileMap);//將排好序的文件重命名(不然離開控制臺就看不到文件的排序了>o<)System.out.println(imageMap);//這里只是用來看具體像素點有多少的,并沒有實際的意義}
是不是很簡單呢?跟大象裝冰箱一樣,只有三個步驟:1.將文件目錄下的所有圖片含有的綠色像素點全部讀取出來,然后將對應的文件名和像素點個數暫存在HashMap里;2.將圖片根據綠色像素點的多少進行排序;3.將排好序的圖片重命名,然后進行排序輸出(Tips:文件會進行重命名的,所有不要直接在源文件上直接玩喔,注意文件的備份);
好了,那我們就直接開始看每個方法具體是怎樣實現的吧,按順序進行講解!(以下大家就注意看代碼中的注釋了,不再做重復的解釋了)
2.讀取圖片像素點的方法private static HashMap<File, Object> getImagePixel(File readFile, HashMap<File, Object> imageMap) {int red = 0;//記錄像素點的紅色int green = 0;//記錄像素點的綠色int blue = 0;//記錄像素點的藍色int counter = 0;//程序計數器BufferedImage bi = null;try {bi = ImageIO.read(readFile);//通過ImageIO來讀取圖片,以便獲取圖片的RGB信息} catch (IOException e) {e.printStackTrace();}int width = bi.getWidth();//獲取圖片的寬度int height = bi.getHeight();//獲取圖片的高度int minx = bi.getMinX();//獲取圖片的坐標起點x軸int miny = bi.getMinY();//獲取圖片的坐標起點y軸for(int i = minx; i < width; i++){for(int j = miny; j < height; j++){int pixel = bi.getRGB(i, j);red = (pixel & 0xff0000) >> 16;//過濾掉圖片的綠色和藍色green = (pixel & 0xff00) >> 8;//過濾掉圖片的綠色blue = (pixel & 0xff);//最后剩下的就是藍色啦if(green - red > 30 && green - blue > 30){//綠色的范圍counter++;}}}imageMap.put(readFile, counter);//將文件和像素點的個數記錄到HashMap中return imageMap;}3.將圖片按照像素點的數量進行排序
由于排序不光在這里可以使用,在其他情況下也可能會使用到(比如說根據文件的創建時間進行排序,都可以用到排序的)。所以我將排序寫成了一個抽象類,其他情況下只需要繼承這個抽象類,然后具體實現自己想要實現的方法就行了!具體的現如下:
抽象類:
package readcolor;import java.io.File;import java.util.HashMap;import java.util.LinkedHashMap;public abstract class HashMapSortUtil {private HashMap<File, Object> sortMap;//全局變量Map,就是主方法需要傳過來進行排序的Map private String prefix;//前綴,用來命名自己的文件 ==> Muspublic abstract LinkedHashMap<File, Object> sortFileMap();//進行排序的方法public abstract void renameFiles(LinkedHashMap<File, Object> linkedTimeMap);//重命名的方法public HashMap<File, Object> getSortMap() {return sortMap;}public void setSortMap(HashMap<File, Object> sortMap) {this.sortMap = sortMap;}public String getPrefix() {return prefix;}public void setPrefix(String prefix) {this.prefix = prefix;}}
子類:
package readcolor;import java.io.File;import java.util.Collections;import java.util.Comparator;import java.util.HashMap;import java.util.LinkedHashMap;import java.util.LinkedList;import java.util.Map.Entry;import java.util.Set;public class HashMapSortUtils extends HashMapSortUtil{private int counter;//計數器,默認從多少開始進行命名 ==> 1private int nameLength;//命名的長度,其實就是計數器之前需要添加幾個0 ==> 3private int nameExpansion = 0;//記錄名字超長了需要進行擴容的次數//prefix(在父類里面),counter,nameLength組成的結果就是對應的Mus001private HashMap<File, File> tempFileMap = new HashMap<File, File>();//記錄在進行重命名時,目標文件有重復時,將源文件拷貝出來與將要命名的名字記錄到HashMap里面public HashMapSortUtils() {//構造方法super();}public HashMapSortUtils(HashMap<File, Object> sortMap, Integer counter, Integer nameLength, String prefix) {//構造方法super();super.setSortMap(sortMap);super.setPrefix(prefix);this.counter = counter;this.nameLength = nameLength;}/** * 將圖片按照像素點個數進行排序的方法 * 參數:無 * 返回值:無 * */@Overridepublic LinkedHashMap<File, Object> sortFileMap() {LinkedHashMap<File,Object> linkedHashMap = new LinkedHashMap<File, Object>();Set<Entry<File, Object>> mapEntries = super.getSortMap().entrySet();//將傳進來需要進行排序的HashMap獲取到每個節點LinkedList<Entry<File, Object>> timeList = new LinkedList<Entry<File, Object>>(mapEntries);//將每個節點放到List集合中,方便利用Collections的方法進行排序Collections.sort(timeList, new Comparator<Entry<File, Object>>() {//利用Comparator接口進行排序@Overridepublic int compare(Entry<File, Object> o1, Entry<File, Object> o2) {if(o1.getValue() == o2.getValue()){//如果兩個文件的綠色像素點相同,就用文件的名字進行比較return o2.getKey().compareTo(o1.getKey());}return ((Integer) o2.getValue()).compareTo((Integer)o1.getValue());//利用文件的綠色像素點進行比較}});for (Entry<File, Object> entry : timeList) {//將排好序之后的文件放到LinkedHashMap中,因為如果方法HashMap中的話,你會發現它的順序又是亂的了-o-linkedHashMap.put(entry.getKey(), entry.getValue());}return linkedHashMap;}/** * 重命名文件的方法 * 參數:linkedTimeMap:需要進行文件重命名的HashMap * 返回值:無 * */@Overridepublic void renameFiles(LinkedHashMap<File, Object> linkedTimeMap) {Set<Entry<File,Object>> entrySet = linkedTimeMap.entrySet();for (Entry<File, Object> entry : entrySet) {renameFile(entry.getKey(), createFileName(entry.getKey())/*根據之前設置文件的名字(counter、nameLength、prefix)生成文件名*/);//重命名文件}//最后重命名剩下的源文件的備份文件renameTempFiles();}/** * 根據之前設置文件的名字(counter、nameLength、prefix)生成文件名 * 參數:oldFile:源文件 * 返回值:生成名字之后的文件 * */private File createFileName(File oldFile) {//通過父類獲取到prefixString prefix = super.getPrefix();//獲取結束String newFileName = '';newFileName += prefix;//先將前綴拼接上int nameLen = String.valueOf(counter).length();//獲取計數器的長度if(nameLen > nameLength){//如果計數器超長了,那么命名的長度(nameLength)就需要進行擴容,不然會出現文件名重復的情況nameLength++;nameExpansion++;//這里記錄是因為,當后面的操作出現錯誤時,這里可能需要將原來的長度進行恢復}if(nameLen <= nameLength){int d_Value = String.valueOf(Math.pow(10, nameLength) - 1).length() - String.valueOf(counter).length() - 2;//計算需要填補的0的個數,這里減2是因為去除double數據后面的.0for (int i = 0; i < d_Value; i++) {newFileName += '0';}}newFileName += counter;//將計數器添加到名字上String oldFileName = oldFile.getName();//獲取源文件的名字String dirName = oldFile.getParentFile().getAbsolutePath();//獲取源文件的上級文件夾的路徑File newFile = new File(dirName + File.separator + newFileName + oldFileName.substring(oldFileName.lastIndexOf('.')));//利用新的文件名生成文件counter++;//計數器需要進行+1return newFile;}/** * 將源文件重命名為新文件的名字 * 參數:oldFile:源文件, newFile:新文件 * 返回值:無 * */private void renameFile(File oldFile, File newFile) {//=================如果源文件和新文件都存在,并且源文件和新文件的名字不相同,那么就需要將源文件備份處理,等其他文件重命名完之后再執行這類文件的重命名操作=================if(oldFile.exists() && oldFile.isFile() && newFile.exists() && newFile.isFile()){if(!newFile.getName().equals(oldFile.getName())){//===============================將源文件做備份處理===============================File oldFileTemp = null;int fileTempCounter = 0;//使用do...while...循環確保暫存文件中沒有重復的名字do{oldFileTemp = new File(oldFile.getAbsolutePath() + fileTempCounter + System.currentTimeMillis());fileTempCounter++;}while(oldFileTemp.exists() && oldFileTemp.isFile());//將源文件的內容復制到備份文件中try{new FileServiceImpl().copyFile(oldFile, oldFileTemp);}catch (Exception e){e.printStackTrace();}//刪除源文件oldFile.delete();//將源文件的備份文件和源文件需要重命名的名字記錄到HashMap里面,最后進行這部分文件的命名操作tempFileMap.put(oldFileTemp, newFile);return;}}//如果目標文件不存在或者目標文件名與源文件名相同,就直接進行重命名的操作if(oldFile.exists() && oldFile.isFile()){if(oldFile.renameTo(newFile)){System.out.println('重命名成功:' + oldFile.getAbsolutePath() + '==>' + newFile.getAbsolutePath());return;}}//重命名失敗就將計數器減一,并且將命名的長度還原到原來的長度System.out.println('====================================重命名失敗:' + oldFile.getAbsolutePath() + '====================================');counter--;nameLength -= nameExpansion;}/** * 重命名剩下的源文件的備份文件 * 參數:無 * 返回值:無 * */private void renameTempFiles() {Set<Entry<File, File>> entrySet = tempFileMap.entrySet();for (Entry<File, File> entry : entrySet) {//調用重命名的方法進行重命名renameFile(entry.getKey(), entry.getValue());}}public int getCounter() {return counter;}public void setCounter(int counter) {this.counter = counter;}public int getNameLength() {return nameLength;}public void setNameLength(int nameLength) {this.nameLength = nameLength;}}
由于counter(計數器)和nameLength(命名的長度)在進行排序和文件重命名的時候會頻繁的使用到,因此我把他們放到了子類里面,避免多次調用父類的getter和setter方法。雖然我代碼里面注釋寫得很清楚,但是還是有一些小伙伴不習慣看注釋,那我稍微做一下解釋,但是代碼的邏輯還是要大家下來看一下!如果在博客上不太方便的話,可以直接copy到eclipse里面或者idea里面進行邏輯的分析!
(1)在主方法或其他方法需要調用到這個類型,可以直接利用該類的構造方法來調用到這個類:
public HashMapSortUtils(HashMap<File, Object> sortMap, Integer counter, Integer nameLength, String prefix){......}
這個構造方法會將需要進行排序和重命名的HashMap加載到該類的成員變量中,該類所有方法都可以調用該HashMap,并且計數器開始的位置(counter)、命名的長度(nameLength)、命名前綴(prefix)都加載到成員變量中,其中HashMap和前綴屬于父類的變量。(相信大多數人都知道,我就亂解釋一番了。。。)(2)將傳進來的HashMap進行排序的方法:
public LinkedHashMap<File, Object> sortFileMap() {......}
該方法就是利用java工具類Collections下面的sort方法進行排序,需要注意的是,最后之所以返回的是一個LinkedHashMap是因為HashMap是無序的,如果排完序還是用HashMap裝排序的結果,那么就有可能沒有達到排序預期的效果
(3)將排好序的HashMap中的文件重命名的方法:
public void renameFiles(LinkedHashMap<File, Object> linkedTimeMap) {......}
該方法主要分為兩個步驟:a.利用createFileName方法將之前設置好的prefix(前綴)、nameLength(命名的長度)、counter(計數器)組成新的名字;b.將HashMap中所有的entry節點利用renameFile方法進行判斷是否可以直接重命名,如果可以直接重命名就直接重命名,如果需要重新命名的文件已經存在就將源文件copy一份出來,然后將拷貝文件和新的名字方法一個HashMap中,等到程序的第c步才執行這部分文件的重命名!c.將之前未進行重命名的源文件進利用renameTempFiles方法行統一的重命名!
4.文件操作的工具類文件操作是一個公共的類,進行文件的復制、刪除、獲取所有文件、創建文件夾等等,都可以寫做一個公共的方法,大家可以自行去了解這個類的作用,這里不再過多的贅述(嘻嘻,又可以偷波懶了),不過我這里是錯誤的示范,我用接口的方式來實現的,真正的生產中是不會這樣做的,因為文件操作是基本上不會變的,這里我只是想單純的聯系一下接口的操做,那么廢話不多說,直接上代碼,都是文件操作的基礎代碼:
文件操作的接口:
package readcolor;import java.io.File;import java.util.List;public interface FileService {void copyFile(String sourcePath, String targetPath) throws Exception;void copyFile(File sourceFile, File targetFile) throws Exception;void mkDirs(String path) throws Exception;List<File> getAllFiles(String sourcePath);void removeFiles(String path);void removeFiles(File sourceFile);}
文件操作的實現類:
package readcolor;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.util.ArrayList;import java.util.List;public class FileServiceImpl implements FileService{@Overridepublic void copyFile(String sourcePath, String targetPath) throws Exception {copyFile(new File(sourcePath), new File(targetPath));}@Overridepublic void copyFile(File sourceFile, File targetFile) throws Exception {FileInputStream fis = new FileInputStream(sourceFile);FileOutputStream fos = new FileOutputStream(targetFile);byte[] buffer = new byte[1024];int len = 0;while((len = fis.read(buffer)) != -1){fos.write(buffer, 0, len);fos.flush();}fos.close();fis.close();}@Overridepublic void mkDirs(String path) throws Exception {File destFile = new File(path);if(!destFile.exists()){destFile.mkdirs();}}@Overridepublic List<File> getAllFiles(String sourcePath) {ArrayList<File> files = new ArrayList<File>();File file = new File(sourcePath);if(file.exists() && !file.isHidden()){if(file.isFile()){files.add(file);}if(file.isDirectory()){File[] fs = file.listFiles();for (File f : fs) {if(!f.isHidden()){if(f.isFile()){files.add(file);}if(f.isDirectory()){files.addAll(getAllFiles(sourcePath + File.separator + f.getName()));}}}}}return files;}@Overridepublic void removeFiles(String path) {removeFiles(new File(path));}@Overridepublic void removeFiles(File sourceFile) {if (!sourceFile.isDirectory()){if(sourceFile.delete()) System.out.println('刪除文件:' + sourceFile.getAbsolutePath() + '成功');}else{File[] files = sourceFile.listFiles();for (File file : files) {if(file.isDirectory()){removeFiles(file);if(file.delete()) System.out.println('刪除文件夾:' + file.getAbsolutePath() + '成功');}else{if(file.delete()) System.out.println('刪除文件:' + file.getAbsolutePath() + '成功');}}}}}
好的,一切準備就緒,那我們直接開始運行代碼,看看效果如何:
先準備好圖片:
然后設置好文件的路徑:
運行java程序:
可以看到所有的文件都已經重新排序,并且已經進行重命名了,看看實際的效果:
是不是感覺前面的圖片要稍微綠一點呢?該程序可以進行重復執行的,暫時沒有出現命名失敗的情況,如果有小伙伴試了然后報錯了,記得留言喔,我看看是啥問題,然后看看能不能再優化一下。。。(聞到了頭發掉落的氣息)
總結最后,我們可以稍微改動幾行代碼,然后將所有的圖片只輸出綠色像素點來做一個直觀的感受:
package readcolor;import java.awt.image.BufferedImage;import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.util.HashMap;import java.util.LinkedHashMap;import javax.imageio.ImageIO;public class ReadColor {private static int count = 0;public static void main(String[] args) {HashMap<File, Object> imageMap = new HashMap<File, Object>();//用hashMap將文件和對應的像素點數量裝到一起File fileDir = new File('D:DownloadTestFile');//將要進行排序的文件目錄File[] listFiles = fileDir.listFiles();for (File readFile : listFiles) {getImagePixel(readFile, imageMap);//獲取圖片的綠色像素點數量的多少}HashMapSortUtils hashMapSortUtils = new HashMapSortUtils(imageMap, 1, 3, 'Mus');LinkedHashMap<File,Object> sortFileMap = hashMapSortUtils.sortFileMap();//將圖片按照像素點的數量進行排序hashMapSortUtils.renameFiles(sortFileMap);//將排好序的文件重命名(不然離開控制臺就看不到文件的排序了>o<)System.out.println(imageMap);}private static HashMap<File, Object> getImagePixel(File readFile, HashMap<File, Object> imageMap) {int red = 0;//記錄像素點的紅色int green = 0;//記錄像素點的綠色int blue = 0;//記錄像素點的藍色int counter = 0;//程序計數器BufferedImage bi = null;try {bi = ImageIO.read(readFile);//通過ImageIO來讀取圖片,以便獲取圖片的RGB信息} catch (IOException e) {e.printStackTrace();}int width = bi.getWidth();//獲取圖片的寬度int height = bi.getHeight();//獲取圖片的高度int minx = bi.getMinX();//獲取圖片的坐標起點x軸int miny = bi.getMinY();//獲取圖片的坐標起點y軸for(int i = minx; i < width; i++){for(int j = miny; j < height; j++){int pixel = bi.getRGB(i, j);red = (pixel & 0xff0000) >> 16;//過濾掉圖片的綠色和藍色green = (pixel & 0xff00) >> 8;//過濾掉圖片的綠色blue = (pixel & 0xff);//最后剩下的就是藍色啦if(green - red > 30 && green - blue > 30){//綠色的范圍counter++;}else{bi.setRGB(i, j, 0xffffff);}}}imageMap.put(readFile, counter);//將文件和像素點的個數記錄到HashMap中try {ImageIO.write(bi, 'jpg', new File('D:DownloadTestFile1' + count +'.jpg'));} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}count++;return imageMap;}}
將圖片的非綠色像素點修改為白色,然后存到一個新的文件夾里,看看是什么效果:
是不是只提取出了綠色像素點呢,大家在這個的基礎上就可以選擇自己喜歡的顏色進行排序了。反正我把這個程序送給我朋友的時候,遭到了他半個小時的感謝(問候),大家只是自己可以玩一玩,千萬別去亂動別人的硬盤喔!(最后的最后,程序并沒有什么實際的使用價值,只是學習了一些新的方法或者技巧,實際上我進行圖像處理的時候都是直接選用photoshop進行操作的,哈哈啊哈哈!)
到此這篇關于java根據圖片中綠色像素點的多少進行排序的文章就介紹到這了,更多相關java綠色像素點排序內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!
相關文章: