java根據(jù)圖片中綠色像素點的多少進行排序
前言
一個朋友被綠了,看見綠色就會很傷感,作為好兄弟的我當然看不下去,感覺有我必要也必須做一點什么。正好我又在寫java程序,那就寫一個小程序幫他把電腦里的圖片排一下順序,根據(jù)綠色的程度進行排序,最后把排好序的圖片偷偷的放到他的電腦里去。為了好兄弟做這么多的事情,正所謂:事了拂衣去,功藏身與名?。。ó斎皇堑剐蚺诺模?/p>
一、利用for循環(huán)讀取圖片
相信學過圖像處理的小伙伴都知道,一張圖片由很多像素組成(當然,矢量圖片除外,大家下來可以了解為啥矢量圖片不是由像素點組成的)。因此,不管是什么圖片我們都看作是一個平面,因為就可以用坐標的方式去讀取圖片啦!那就廢話不多說直接開始!
二、代碼的邏輯
1.先給大家看看主方法里面都有一些什么內(nèi)容
public static void main(String[] args) {
HashMap<File, Object> imageMap = new HashMap<File, Object>();//用hashMap將文件和對應的像素點數(shù)量裝到一起
File fileDir = new File("D:\\Download\\TestFile");//將要進行排序的文件目錄
File[] listFiles = fileDir.listFiles();
for (File readFile : listFiles) {
getImagePixel(readFile, imageMap);//獲取圖片的綠色像素點數(shù)量的多少
}
HashMapSortUtils hashMapSortUtils = new HashMapSortUtils(imageMap, 1, 3, "Mus");
LinkedHashMap<File,Object> sortFileMap = hashMapSortUtils.sortFileMap();//將圖片按照像素點的數(shù)量進行排序
hashMapSortUtils.renameFiles(sortFileMap);//將排好序的文件重命名(不然離開控制臺就看不到文件的排序了>o<)
System.out.println(imageMap);//這里只是用來看具體像素點有多少的,并沒有實際的意義
}
是不是很簡單呢?跟大象裝冰箱一樣,只有三個步驟:
1.將文件目錄下的所有圖片含有的綠色像素點全部讀取出來,然后將對應的文件名和像素點個數(shù)暫存在HashMap里;
2.將圖片根據(jù)綠色像素點的多少進行排序;
3.將排好序的圖片重命名,然后進行排序輸出(Tips:文件會進行重命名的,所有不要直接在源文件上直接玩喔,注意文件的備份);
好了,那我們就直接開始看每個方法具體是怎樣實現(xiàn)的吧,按順序進行講解?。ㄒ韵麓蠹揖妥⒁饪创a中的注釋了,不再做重復的解釋了)
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;//程序計數(shù)器
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);//將文件和像素點的個數(shù)記錄到HashMap中
return imageMap;
}
3.將圖片按照像素點的數(shù)量進行排序
由于排序不光在這里可以使用,在其他情況下也可能會使用到(比如說根據(jù)文件的創(chuàng)建時間進行排序,都可以用到排序的)。所以我將排序?qū)懗闪艘粋€抽象類,其他情況下只需要繼承這個抽象類,然后具體實現(xiàn)自己想要實現(xiàn)的方法就行了!具體的現(xiàn)如下:
抽象類:
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;//前綴,用來命名自己的文件 ==> Mus
public 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;//計數(shù)器,默認從多少開始進行命名 ==> 1
private int nameLength;//命名的長度,其實就是計數(shù)器之前需要添加幾個0 ==> 3
private int nameExpansion = 0;//記錄名字超長了需要進行擴容的次數(shù)
//prefix(在父類里面),counter,nameLength組成的結(jié)果就是對應的Mus001
private HashMap<File, File> tempFileMap = new HashMap<File, File>();//記錄在進行重命名時,目標文件有重復時,將源文件拷貝出來與將要命名的名字記錄到HashMap里面
public HashMapSortUtils() {//構(gòu)造方法
super();
}
public HashMapSortUtils(HashMap<File, Object> sortMap, Integer counter, Integer nameLength, String prefix) {//構(gòu)造方法
super();
super.setSortMap(sortMap);
super.setPrefix(prefix);
this.counter = counter;
this.nameLength = nameLength;
}
/**
* 將圖片按照像素點個數(shù)進行排序的方法
* 參數(shù):無
* 返回值:無
* */
@Override
public LinkedHashMap<File, Object> sortFileMap() {
LinkedHashMap<File,Object> linkedHashMap = new LinkedHashMap<File, Object>();
Set<Entry<File, Object>> mapEntries = super.getSortMap().entrySet();//將傳進來需要進行排序的HashMap獲取到每個節(jié)點
LinkedList<Entry<File, Object>> timeList = new LinkedList<Entry<File, Object>>(mapEntries);//將每個節(jié)點放到List集合中,方便利用Collections的方法進行排序
Collections.sort(timeList, new Comparator<Entry<File, Object>>() {//利用Comparator接口進行排序
@Override
public 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中的話,你會發(fā)現(xiàn)它的順序又是亂的了-o-
linkedHashMap.put(entry.getKey(), entry.getValue());
}
return linkedHashMap;
}
/**
* 重命名文件的方法
* 參數(shù):linkedTimeMap:需要進行文件重命名的HashMap
* 返回值:無
* */
@Override
public 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())/*根據(jù)之前設置文件的名字(counter、nameLength、prefix)生成文件名*/);//重命名文件
}
//最后重命名剩下的源文件的備份文件
renameTempFiles();
}
/**
* 根據(jù)之前設置文件的名字(counter、nameLength、prefix)生成文件名
* 參數(shù):oldFile:源文件
* 返回值:生成名字之后的文件
* */
private File createFileName(File oldFile) {
//通過父類獲取到prefix
String prefix = super.getPrefix();
//獲取結(jié)束
String newFileName = "";
newFileName += prefix;//先將前綴拼接上
int nameLen = String.valueOf(counter).length();//獲取計數(shù)器的長度
if(nameLen > nameLength){//如果計數(shù)器超長了,那么命名的長度(nameLength)就需要進行擴容,不然會出現(xiàn)文件名重復的情況
nameLength++;
nameExpansion++;//這里記錄是因為,當后面的操作出現(xiàn)錯誤時,這里可能需要將原來的長度進行恢復
}
if(nameLen <= nameLength){
int d_Value = String.valueOf(Math.pow(10, nameLength) - 1).length() - String.valueOf(counter).length() - 2;//計算需要填補的0的個數(shù),這里減2是因為去除double數(shù)據(jù)后面的.0
for (int i = 0; i < d_Value; i++) {
newFileName += "0";
}
}
newFileName += counter;//將計數(shù)器添加到名字上
String oldFileName = oldFile.getName();//獲取源文件的名字
String dirName = oldFile.getParentFile().getAbsolutePath();//獲取源文件的上級文件夾的路徑
File newFile = new File(dirName + File.separator + newFileName + oldFileName.substring(oldFileName.lastIndexOf(".")));//利用新的文件名生成文件
counter++;//計數(shù)器需要進行+1
return newFile;
}
/**
* 將源文件重命名為新文件的名字
* 參數(shù):oldFile:源文件, newFile:新文件
* 返回值:無
* */
private void renameFile(File oldFile, File newFile) {
//=================如果源文件和新文件都存在,并且源文件和新文件的名字不相同,那么就需要將源文件備份處理,等其他文件重命名完之后再執(zhí)行這類文件的重命名操作=================
if(oldFile.exists() && oldFile.isFile() && newFile.exists() && newFile.isFile()){
if(!newFile.getName().equals(oldFile.getName())){
//===============================將源文件做備份處理===============================
File oldFileTemp = null;
int fileTempCounter = 0;
//使用do...while...循環(huán)確保暫存文件中沒有重復的名字
do{
oldFileTemp = new File(oldFile.getAbsolutePath() + fileTempCounter + System.currentTimeMillis());
fileTempCounter++;
}while(oldFileTemp.exists() && oldFileTemp.isFile());
//將源文件的內(nèi)容復制到備份文件中
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;
}
}
//重命名失敗就將計數(shù)器減一,并且將命名的長度還原到原來的長度
System.out.println("====================================重命名失敗:" + oldFile.getAbsolutePath() + "====================================");
counter--;
nameLength -= nameExpansion;
}
/**
* 重命名剩下的源文件的備份文件
* 參數(shù):無
* 返回值:無
* */
private void renameTempFiles() {
Set<Entry<File, File>> entrySet = tempFileMap.entrySet();
for (Entry<File, File> entry : entrySet) {
//調(diào)用重命名的方法進行重命名
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(計數(shù)器)和nameLength(命名的長度)在進行排序和文件重命名的時候會頻繁的使用到,因此我把他們放到了子類里面,避免多次調(diào)用父類的getter和setter方法。雖然我代碼里面注釋寫得很清楚,但是還是有一些小伙伴不習慣看注釋,那我稍微做一下解釋,但是代碼的邏輯還是要大家下來看一下!如果在博客上不太方便的話,可以直接copy到eclipse里面或者idea里面進行邏輯的分析!
(1)在主方法或其他方法需要調(diào)用到這個類型,可以直接利用該類的構(gòu)造方法來調(diào)用到這個類:
public HashMapSortUtils(HashMap<File, Object> sortMap, Integer counter, Integer nameLength, String prefix){
......
}
這個構(gòu)造方法會將需要進行排序和重命名的HashMap加載到該類的成員變量中,該類所有方法都可以調(diào)用該HashMap,并且計數(shù)器開始的位置(counter)、命名的長度(nameLength)、命名前綴(prefix)都加載到成員變量中,其中HashMap和前綴屬于父類的變量。(相信大多數(shù)人都知道,我就亂解釋一番了。。。)
(2)將傳進來的HashMap進行排序的方法:
public LinkedHashMap<File, Object> sortFileMap() {
......
}
該方法就是利用java工具類Collections下面的sort方法進行排序,需要注意的是,最后之所以返回的是一個LinkedHashMap是因為HashMap是無序的,如果排完序還是用HashMap裝排序的結(jié)果,那么就有可能沒有達到排序預期的效果
(3)將排好序的HashMap中的文件重命名的方法:
public void renameFiles(LinkedHashMap<File, Object> linkedTimeMap) {
......
}
該方法主要分為兩個步驟:a.利用createFileName方法將之前設置好的prefix(前綴)、nameLength(命名的長度)、counter(計數(shù)器)組成新的名字;b.將HashMap中所有的entry節(jié)點利用renameFile方法進行判斷是否可以直接重命名,如果可以直接重命名就直接重命名,如果需要重新命名的文件已經(jīng)存在就將源文件copy一份出來,然后將拷貝文件和新的名字方法一個HashMap中,等到程序的第c步才執(zhí)行這部分文件的重命名!c.將之前未進行重命名的源文件進利用renameTempFiles方法行統(tǒng)一的重命名!
4.文件操作的工具類
文件操作是一個公共的類,進行文件的復制、刪除、獲取所有文件、創(chuàng)建文件夾等等,都可以寫做一個公共的方法,大家可以自行去了解這個類的作用,這里不再過多的贅述(嘻嘻,又可以偷波懶了),不過我這里是錯誤的示范,我用接口的方式來實現(xiàn)的,真正的生產(chǎn)中是不會這樣做的,因為文件操作是基本上不會變的,這里我只是想單純的聯(lián)系一下接口的操做,那么廢話不多說,直接上代碼,都是文件操作的基礎代碼:
文件操作的接口:
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);
}
文件操作的實現(xiàn)類:
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{
@Override
public void copyFile(String sourcePath, String targetPath) throws Exception {
copyFile(new File(sourcePath), new File(targetPath));
}
@Override
public 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();
}
@Override
public void mkDirs(String path) throws Exception {
File destFile = new File(path);
if(!destFile.exists()){
destFile.mkdirs();
}
}
@Override
public 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;
}
@Override
public void removeFiles(String path) {
removeFiles(new File(path));
}
@Override
public 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程序:

可以看到所有的文件都已經(jīng)重新排序,并且已經(jīng)進行重命名了,看看實際的效果:

是不是感覺前面的圖片要稍微綠一點呢?該程序可以進行重復執(zhí)行的,暫時沒有出現(xiàn)命名失敗的情況,如果有小伙伴試了然后報錯了,記得留言喔,我看看是啥問題,然后看看能不能再優(yōu)化一下。。。(聞到了頭發(fā)掉落的氣息)
總結(jié)
最后,我們可以稍微改動幾行代碼,然后將所有的圖片只輸出綠色像素點來做一個直觀的感受:
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將文件和對應的像素點數(shù)量裝到一起
File fileDir = new File("D:\\Download\\TestFile");//將要進行排序的文件目錄
File[] listFiles = fileDir.listFiles();
for (File readFile : listFiles) {
getImagePixel(readFile, imageMap);//獲取圖片的綠色像素點數(shù)量的多少
}
HashMapSortUtils hashMapSortUtils = new HashMapSortUtils(imageMap, 1, 3, "Mus");
LinkedHashMap<File,Object> sortFileMap = hashMapSortUtils.sortFileMap();//將圖片按照像素點的數(shù)量進行排序
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;//程序計數(shù)器
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);//將文件和像素點的個數(shù)記錄到HashMap中
try {
ImageIO.write(bi, "jpg", new File("D:\\Download\\TestFile1\\" + count +".jpg"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
count++;
return imageMap;
}
}
將圖片的非綠色像素點修改為白色,然后存到一個新的文件夾里,看看是什么效果:

是不是只提取出了綠色像素點呢,大家在這個的基礎上就可以選擇自己喜歡的顏色進行排序了。反正我把這個程序送給我朋友的時候,遭到了他半個小時的感謝(問候),大家只是自己可以玩一玩,千萬別去亂動別人的硬盤喔?。ㄗ詈蟮淖詈?,程序并沒有什么實際的使用價值,只是學習了一些新的方法或者技巧,實際上我進行圖像處理的時候都是直接選用photoshop進行操作的,哈哈啊哈哈?。?/p>
到此這篇關于java根據(jù)圖片中綠色像素點的多少進行排序的文章就介紹到這了,更多相關java綠色像素點排序內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot+Hibernate實現(xiàn)自定義數(shù)據(jù)驗證及異常處理
這篇文章主要為大家介紹了SpringBoot如何整合Hibernate自定義數(shù)據(jù)驗證及多種方式異常處理,文中的示例代碼講解詳細,感興趣的可以了解一下2022-04-04
SpringBoot+Redis執(zhí)行l(wèi)ua腳本的5種方式總結(jié)
Lua是一種快速、輕量級的腳本語言,廣泛應用于各種領域,包括數(shù)據(jù)庫,Redis作為一個內(nèi)嵌Lua解釋器的NoSQL數(shù)據(jù)庫,允許通過Lua腳本在服務器端執(zhí)行一些復雜的操作,本文給大家介紹了使用SpringBoot Redis執(zhí)行l(wèi)ua腳本的五種方式,需要的朋友可以參考下2023-11-11
springboot使用alibaba的druid數(shù)據(jù)庫連接池錯誤的問題及解決
這篇文章主要介紹了springboot使用alibaba的druid數(shù)據(jù)庫連接池錯誤的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02
SpringMVC使用hibernate-validator進行參數(shù)校驗最佳實踐記錄
這篇文章主要介紹了SpringMVC使用hibernate-validator進行參數(shù)校驗最佳實踐,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-05-05
Java代碼的三根頂梁柱:循環(huán)結(jié)構(gòu)
這篇文章主要介紹了JAVA 循環(huán)結(jié)構(gòu)的相關資料,文中講解的非常細致,示例代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下2021-08-08
Spring boot中自定義Json參數(shù)解析器的方法
這篇文章主要介紹了Spring boot中自定義Json參數(shù)解析器的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01
IntelliJ IDEA(或者JetBrains PyCharm)中彈出"IntelliJ IDEA License
今天小編就為大家分享一篇關于IntelliJ IDEA(或者JetBrains PyCharm)中彈出"IntelliJ IDEA License Activation"的解決辦法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-10-10

