驗證碼處理演算法(一)
阿新 • • 發佈:2018-12-21
在面對那種有許多幹擾線或者干擾點的驗證碼,或者各種各樣的驗證碼的時候,往往一個閥值是無法精確的處理圖形驗證碼的,這裡,我們主要使用一個範圍縮圈進行畫素點的比對,因為之前有使用計算畫素的平均值K作為閾值,但是會導致部分物件畫素或者背景畫素丟失,故這個方案暫且擱置。
我們知道每一個畫素點都有自己的畫素值,但是對於一張驗證碼來說,同一個字母顏色或深或淺,其畫素點的值都會有差異,所以我們需要一個容錯範圍,我們可以指定,在某個範圍差內的值,都是這個圖片字母的值。然後我們讓這個閥值在一個範圍內不斷遞增,從而得到不同閥值下的二值化圖片。
這裡有一個處理前和處理後圖片的對比:
處理前的圖片
處理後我們得到很多張圖片,從中選出最完整的一張圖片:
因為機器學習的前期圖片還是需要人工篩選一下的。
以下是我們處理圖片的程式碼:
import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; public class PicDeal { //todo splitNums可以根據你給到的圖片色差進行調整,在你自己使用時,可以針對splitNums做一個迴圈,每次加多少,得到不同的色差比的二值化後的圖片,因為不同的圖片可能干擾線、干擾點顏色原因,二值化後會有差異 //todo splitWidthNum:把圖片根據長度切分的分數,這個可以根據你圖片中的數字個數進行切分 public static int splitNums=2100000; public static int beginNum=100000; public static int endNum=4000000; public static final int splitWidthNum=1; public static void main(String[] args) { //todo 需要處理圖片的路徑 String path="C://Users/admin/Desktop/微信搜狗驗證碼/yzmsg/3.jpg"; try{ splitPic(path); }catch (Exception e){ e.printStackTrace(); } } //todo 分割圖片 public static void splitPic(String picFile) throws Exception{ //todo 分割圖片 // BufferedImage img = ImageIO.read(new File(picFile)); Image image = ImageIO.read(new File(picFile)); for (int i = 0; i < splitWidthNum; i++) { String filename="F://test/test3_"+i+".jpg"; ImageIO.write(toBufferedImage(image), "JPG", new File(filename)); //todo 分割後的圖片處理 for (int j=beginNum;j<=endNum;j+=25000){ Image thisimg = ImageIO.read(new File(filename)); BufferedImage imgDeal=removeBackgroud(thisimg,j,i); imgDeal=DeletePassPoint.deletepoints(imgDeal); //todo 分割後圖片寫出去 String filename2="F://test/test3_"+i+"imgDeal_畫素分割_"+j+".jpg"; ImageIO.write(imgDeal, "JPG", new File(filename2)); } } } //todo 圖片處理演算法 public static BufferedImage removeBackgroud(Image img2,int j,int i)throws Exception { BufferedImage img=toBufferedImage(img2); String filename="F://test/test3_"+i+"_畫素分割_"+j+".jpg"; ImageIO.write(img, "JPG", new File(filename)); splitNums=j; System.out.println("splitNums==========="+splitNums); img = img.getSubimage(1, 1, img.getWidth()-2, img.getHeight()-2); int width = img.getWidth(); int height = img.getHeight(); double subWidth = (double) width; Map<Integer, Integer> map = new HashMap<Integer, Integer>(); //todo 以下是對圖片進行二值化處理,在這裡我的思路是規定,色差範圍在splitNums到負splitNums之間的,算是同色,放入同一個色值,放入一個map中, //todo map中的Key放色值,value放這個色值得個數,後期就根據這個色值來對驗證碼進行二值化 for (int x = (int) (1 + 0 * subWidth); x < (0 + 1) * subWidth && x < width - 1; ++x) { for (int y = 0; y < height; ++y) { if (isWhite(img.getRGB(x, y)) == 1){ continue; } Map<Integer, Integer> map2 = new HashMap<Integer, Integer>(); for (Integer color : map.keySet()) { map2.put(color,map.get(color)); } boolean hasnewColor=false; for (Integer color : map2.keySet()) { // System.out.println(Math.abs(color)-Math.abs(img.getRGB(x, y))); // Math.abs(color)-Math.abs(img.getRGB(x, y)))<splitNums&&Math.abs(color)-Math.abs(img.getRGB(x, y))>-splitNums if (Math.abs(color)-Math.abs(img.getRGB(x, y))<splitNums&&Math.abs(color)-Math.abs(img.getRGB(x, y))>-splitNums){ map.put(color, map.get(color) + 1); hasnewColor=true; } } if (!hasnewColor){ map.put(img.getRGB(x, y), 1); } if (map.isEmpty()){ map.put(img.getRGB(x, y), 1); } } } System.out.println("=============================="); int max = 0; int colorMax = 0; for (Integer color : map.keySet()) { if (max < map.get(color)) { max = map.get(color); colorMax = color; } } map.remove(colorMax); max = 0; int colorMax2 = 0; for (Integer color : map.keySet()) { if (max < map.get(color)) { max = map.get(color); colorMax2 = color; } } map.remove(colorMax2); // int colorMax3 = 0; // for (Integer color : map.keySet()) { // if (max < map.get(color)) { // max = map.get(color); // colorMax3 = color; // } // } // colorMax=colorMax2; // colorMax2=colorMax3; //todo 核心演算法 for (int x = (int) (1 + 0 * subWidth); x < (0 + 1) * subWidth&& x < width - 1; ++x) { for (int y = 0; y < height; ++y) { int ress=Math.abs(img.getRGB(x, y))-Math.abs(colorMax); int ress_2=Math.abs(Math.abs(colorMax)-Math.abs(img.getRGB(x, y))); int ress2=Math.abs(img.getRGB(x, y))-Math.abs(colorMax2); if (ress>0&&ress2>0){ if (ress>ress2){ if ((ress2<splitNums&&ress2>-splitNums)) { img.setRGB(x, y, Color.BLACK.getRGB()); } else { img.setRGB(x, y, Color.WHITE.getRGB()); } }else{ // img.setRGB(x, y, Color.WHITE.getRGB()); if (ress2<splitNums&&ress2>ress) { img.setRGB(x, y, Color.BLACK.getRGB()); } else { img.setRGB(x, y, Color.WHITE.getRGB()); } } }else if (ress<0&&ress2<0){ ress=Math.abs(ress); ress2=Math.abs(ress2); if (ress>ress2){ if ((ress2<splitNums&&ress2>-splitNums)) { img.setRGB(x, y, Color.BLACK.getRGB()); } else { img.setRGB(x, y, Color.WHITE.getRGB()); } }else{ if (ress2<splitNums&&ress2>ress) { img.setRGB(x, y, Color.BLACK.getRGB()); } else { img.setRGB(x, y, Color.WHITE.getRGB()); } } }else if (ress>0&&ress2<0){ if (Math.abs(ress)>Math.abs(ress2)){ if ((Math.abs(ress2)>splitNums&&ress2<-splitNums)) { img.setRGB(x, y, Color.BLACK.getRGB()); } else { img.setRGB(x, y, Color.WHITE.getRGB()); } }else{ img.setRGB(x, y, Color.WHITE.getRGB()); // if (splitNums<) } }else{ if (Math.abs(ress)>Math.abs(ress2)){ if ((Math.abs(ress2)>splitNums&&ress2<-splitNums)) { img.setRGB(x, y, Color.BLACK.getRGB()); } else { img.setRGB(x, y, Color.WHITE.getRGB()); } }else{ img.setRGB(x, y, Color.WHITE.getRGB()); // if (splitNums<) } // img.setRGB(x, y, Color.BLACK.getRGB()); } } } return img; } //todo 判斷是否為白色的方法 爬蟲 驗證碼處理 public static int isWhite(int colorInt) { Color color = new Color(colorInt); if (color.getRed() + color.getGreen() + color.getBlue()>600) { return 1; } return 0; } //todo image轉bufferImage public static BufferedImage toBufferedImage(Image image) { if (image instanceof BufferedImage) { return (BufferedImage)image; } // 載入所有畫素 image = new ImageIcon(image).getImage(); BufferedImage bimage = null; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); try { int transparency = Transparency.OPAQUE; // 建立buffer影象 GraphicsDevice gs = ge.getDefaultScreenDevice(); GraphicsConfiguration gc = gs.getDefaultConfiguration(); bimage = gc.createCompatibleImage( image.getWidth(null), image.getHeight(null), transparency); } catch (HeadlessException e) { e.printStackTrace(); } if (bimage == null) { int type = BufferedImage.TYPE_INT_RGB; bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type); } // 複製 Graphics g = bimage.createGraphics(); // 賦值 g.drawImage(image, 0, 0, null); g.dispose(); return bimage; } }
在這裡我們預設RGB最多的為背景色,所以依照RGB顏色第二多的為準進行歸類劃值。
在這裡面我們也進行了一波簡單的處理,就是對得到的圖片進行單獨點的刪除,因為處理後的圖片會有類似這張圖一樣的小點:
,針對這類的小點,我們預設去掉。
演算法其實很簡單,就是當判定上下左右其中3個方位為空時,此點算離散點。
程式碼如下:
import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.io.File; public class DeletePassPoint { public static BufferedImage deletepoints(BufferedImage img){ //todo 為白色的畫素點rgb值 // int[] whitePointNum={-257,-1,-770,-1027,-514}; int width = img.getWidth(); int height = img.getHeight(); double subWidth = (double) width; ColorModel cm = img.getColorModel(); BufferedImage imgNew=new BufferedImage(cm,cm.createCompatibleWritableRaster(img.getWidth(),img.getHeight()),cm.isAlphaPremultiplied(), null); for (int x = (int) (1 + 0 * subWidth); x < (0 + 1) * subWidth && x < width - 1; ++x) { for (int y = 1; y < height-1; ++y) { int leftRgb=img.getRGB(x-1,y); int rightRgb=img.getRGB(x+1,y); int buttonRgb=img.getRGB(x,y-1); int headRgb=img.getRGB(x,y+1); int judgy=0; //todo 對自己本身畫素點周邊進行檢測 if (isWhite(leftRgb)==0){ judgy++; } if (isWhite(rightRgb)==0){ judgy++; } if (isWhite(buttonRgb)==0){ judgy++; } if (isWhite(headRgb)==0){ judgy++; } //todo 當判定上下左右其中3個方位為空時,此點算離散點 if (judgy>=3){ // imgNew.setRGB(x, y, Color.BLACK.getRGB()); imgNew.setRGB(x, y, Color.WHITE.getRGB()); }else{ // imgNew.setRGB(x, y, Color.WHITE.getRGB()); imgNew.setRGB(x, y, Color.BLACK.getRGB()); } } } return imgNew; } //todo 判斷是否為白色的方法 爬蟲 驗證碼處理 public static int isWhite(int colorInt) { Color color = new Color(colorInt); if (color.getRed() + color.getGreen() + color.getBlue()>600) { return 1; } return 0; } }
這樣下來,處理出來的圖片就是我們剛才貼出來的樣子了。當然,這個方案還有很多需要改進的地方。
比如:當背景顏色RGB值並不是最多的時候,這樣就會出現誤刪除。還有就是驗證碼RGB色和背景顏色相差不大時,得到的也是失真的圖片。
當然,後期如果有什麼更好的方案處理圖片,會不斷更新,也歡迎各種觀眾大佬爺提出建議,不斷精進!