<|start_filename|>src/com/by_syk/graphiccr/core/GraphicC1Translator.java<|end_filename|>
/**
 * Copyright 2016 By_syk
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.by_syk.graphiccr.core;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;

import com.by_syk.graphiccr.util.ExtraUtil;

/**
 * 第1类图形验证码识别
 * <br />针对截至 2016-11-22 为止东北大学秦皇岛分校教务管理系统登录用的验证码
 * <br />图形尺寸为 60*20
 * 
 * @author By_syk
 */
public class GraphicC1Translator {
    private static GraphicC1Translator translator = null;
    
    private Map<BufferedImage, Character> trainMap = null;
    
    /**
     * 元字符宽度
     */
    private static final int UNIT_W = 10;
    /**
     * 元字符高度
     */
    private static final int UNIT_H = 12;
    
    /**
     * 有效像素颜色值
     */
    private static final int TARGET_COLOR = Color.BLACK.getRGB();
    
    /**
     * 无效像素颜色值
     */
    private static final int USELESS_COLOR = Color.WHITE.getRGB();
    
    private GraphicC1Translator() {}
    
    public static GraphicC1Translator getInstance() {
        if (translator == null) {
            translator = new GraphicC1Translator();
        }
        
        return translator;
    }
    
    /**
     * 目标像素判断
     * <br />（基于亮度）
     * 
     * @param colorInt
     * @return
     */
    private boolean isTarget(int colorInt) {
        Color color = new Color(colorInt);
        float[] hsb = new float[3];
        Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), hsb);
        return hsb[2] < 0.6f; // 亮度
    }

    /**
     * 去噪
     * 
     * @param picFile 图形验证码文件
     * @return
     * @throws Exception
     */
    private BufferedImage denoise(File picFile) throws Exception {
        BufferedImage img = ImageIO.read(picFile);
        int width = img.getWidth();
        int height = img.getHeight();
        for (int x = 0; x < width; ++x) {
            for (int y = 0; y < height; ++y) {
                if (isTarget(img.getRGB(x, y))) {
                    img.setRGB(x, y, TARGET_COLOR);
                } else {
                    img.setRGB(x, y, USELESS_COLOR);
                }
            }
        }
        return img;
    }

    /**
     * 分割元字符
     * 
     * @param img
     * @return
     * @throws Exception
     */
    private List<BufferedImage> split(BufferedImage img) throws Exception {
        List<BufferedImage> subImgs = new ArrayList<BufferedImage>();
        subImgs.add(img.getSubimage(5, 4, UNIT_W, UNIT_H));
        subImgs.add(img.getSubimage(18, 4, UNIT_W, UNIT_H));
        subImgs.add(img.getSubimage(32, 4, UNIT_W, UNIT_H));
        subImgs.add(img.getSubimage(44, 4, UNIT_W, UNIT_H));
        return subImgs;
    }
    
    /**
     * 取出训练数据
     * 
     * @return
     * @throws Exception
     */
    private Map<BufferedImage, Character> loadTrainData() throws Exception {
        if (trainMap == null) {
            trainMap = new HashMap<>();
            String trainLog = ExtraUtil.readFile(this.getClass()
                    .getResourceAsStream("/resources/img/1/1.txt"));
            if (trainLog == null) {
                return trainMap;
            }
            for (String name : trainLog.split("\n")) {
                trainMap.put(ImageIO.read(this.getClass()
                        .getResourceAsStream("/resources/img/1/" + name)), name.charAt(0));
            }
        }
        
        return trainMap;
    }

    /**
     * 训练
     */
    public void train() {
        // 由于样本具有很强的规律性，已通过 PS 训练完成
    }

    /**
     * 单元识别
     * 
     * @param img
     * @param trainImgMap
     * @return
     */
    private char recognize(BufferedImage img, Map<BufferedImage, Character> trainImgMap) {
        char result = ' ';
        int width = img.getWidth();
        int height = img.getHeight();
        int min = width * height; // 最小差异像素数
        for (BufferedImage bi : trainImgMap.keySet()) {
            int count = 0; // 差异像素数
            for (int x = 0; x < width; ++x) {
                for (int y = 0; y < height; ++y) {
                    count += (img.getRGB(x, y) != bi.getRGB(x, y) ? 1 : 0);
                    if (count >= min) {
                        break;
                    }
                }
            }
            if (count < min) {
                min = count;
                result = trainImgMap.get(bi);
            }
        }
        return result;
    }

    /**
     * 识别
     * 
     * @param picFile 图形验证码文件
     * @return
     */
    public String translate(File picFile) {
        String result = "";
        try {
            BufferedImage img = denoise(picFile);
            List<BufferedImage> listImg = split(img);
            Map<BufferedImage, Character> map = loadTrainData();
            for (BufferedImage bi : listImg) {
                result += recognize(bi, map);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

<|start_filename|>src/com/by_syk/graphiccr/core/GraphicCTranslator.java<|end_filename|>
/**
 * Copyright 2016 By_syk
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.by_syk.graphiccr.core;

import java.io.File;

import com.by_syk.graphiccr.util.ExtraUtil;

public class GraphicCTranslator {
    /**
     * 第1类图形验证码识别
     * <br />针对截至 2016-11-22 为止东北大学秦皇岛分校教务系统登录用的验证码
     * <br />图形尺寸为 60*20
     */
    public static final int TYPE_1 = 1;
    
    /**
     * 第2类图形验证码识别
     * <br />针对截至 2016-11-22 为止成都医学院、四川理工学院教务管理系统登录用的验证码
     * <br />图形尺寸为 72*27
     */
    public static final int TYPE_2 = 2;
    
    /**
     * 第3类图形验证码识别
     * <br />针对截至 2016-11-22 为止蚌埠医学院教务网络管理系统登录用的验证码
     * <br />图形尺寸为 122*54
     */
    public static final int TYPE_3 = 3;
    
    /**
     * 第4类图形验证码识别
     * <br />针对截至 2016-12-01 为止山东交通学院综合教务系统登录用的验证码
     * <br />图形尺寸为 60*20
     */
    public static final int TYPE_4 = 4;
    
    /**
     * 第5类图形验证码识别
     * <br />针对截至 2016-12-03 为止南昌大学教学一体化服务平台登录用的验证码
     * <br />图形尺寸为 62*22
     */
    public static final int TYPE_5 = 5;
    
    /**
     * 第6类图形验证码识别
     * <br />针对截至 2016-12-07 为止安徽工业大学教务管理系统登录用的验证码
     * <br />图形尺寸为 60*22
     */
    public static final int TYPE_6 = 6;
    
    /**
     * 第7类图形验证码识别
     * <br />针对截至 2016-12-12 为止青岛农业大学教学一体化服务平台登录用的验证码
     * <br />图形尺寸为 62*22
     */
    public static final int TYPE_7 = 7;
    
    /**
     * 识别指定文件的图形验证码
     * 
     * @param picFile
     * @param type
     * @return
     */
    public static String translate(File picFile, int type) {
        switch (type) {
        case TYPE_1:
            return GraphicC1Translator.getInstance().translate(picFile);
        case TYPE_2:
            return GraphicC2Translator.getInstance().translate(picFile);
        case TYPE_3:
            return GraphicC3Translator.getInstance().translate(picFile);
        case TYPE_4:
            return GraphicC4Translator.getInstance().translate(picFile);
        case TYPE_5:
            return GraphicC5Translator.getInstance().translate(picFile);
        case TYPE_6:
            return GraphicC6Translator.getInstance().translate(picFile);
        case TYPE_7:
            return GraphicC7Translator.getInstance().translate(picFile);
        }
        return "";
    }
    
    /**
     * 识别指定文件的图形验证码
     * 
     * @param picPath
     * @param type
     * @return
     */
    public static String translate(String picPath, int type) {
        if (picPath == null) {
            return "";
        }
        return translate(new File(picPath), type);
    }
    
    /**
     * 识别指定链接的图形验证码
     * 
     * @param urlStr
     * @param cacheFile
     * @param type
     * @return
     */
    public static String translate(String urlStr, File cacheFile, int type) {
        if (urlStr == null) {
            return "";
        }
        boolean ok = ExtraUtil.downloadFile(urlStr, cacheFile);
        if (ok) {
            return translate(cacheFile, type);
        }
        return "";
    }
}


<|start_filename|>src/com/by_syk/graphiccr/util/ExtraUtil.java<|end_filename|>
/**
 * Copyright 2016 By_syk
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.by_syk.graphiccr.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;

public class ExtraUtil {
    public static boolean downloadFile(String urlStr, File saveFile) {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            URL url = new URL(urlStr);
            URLConnection urlConnection = url.openConnection();
            inputStream = urlConnection.getInputStream();
            outputStream = new FileOutputStream(saveFile);
            byte[] buffer = new byte[1024];
            int len;
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        
        return false;
    }
    
    public static String readFile(InputStream inputStream) {
        if (inputStream == null) {
            return null;
        }

        StringBuilder sbData = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String buffer;
            while ((buffer = bufferedReader.readLine()) != null) {
                sbData.append(buffer).append("\n");
            }
            if (sbData.length() > 0) {
                sbData.setLength(sbData.length() - 1);
            }

            return sbData.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return null;
    }
}


<|start_filename|>src/com/by_syk/graphiccr/core/GraphicC2Translator.java<|end_filename|>
/**
 * Copyright 2016 By_syk
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.by_syk.graphiccr.core;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.imageio.ImageIO;

/**
 * 第2类图形验证码识别
 * <br />针对截至 2016-11-22 为止成都医学院、四川理工学院教务管理系统登录用的验证码
 * <br />图形尺寸为 72*27
 * 
 * @author By_syk
 */
public class GraphicC2Translator {
    private static GraphicC2Translator translator = null;
    
    private BufferedImage trainImg = null;
    
    /**
     * 元字符宽度
     */
    private static final int UNIT_W = 13;
    
    /**
     * 元字符高度
     */
    private static final int UNIT_H = 22;
    
    /**
     * 训练元字符数
     */
    private static final int TRAIN_NUM = 32;
    
    /**
     * 所有元字符
     */
    private static final char[] TRAIN_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8',/* '9',*/
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
            /*'o', */'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y'/*, 'z'*/};
    
    /**
     * 有效像素颜色值
     */
    private static final int TARGET_COLOR = Color.BLACK.getRGB();
    
    /**
     * 无效像素颜色值
     */
    private static final int USELESS_COLOR = Color.WHITE.getRGB();
    
    private GraphicC2Translator() {}
    
    public static GraphicC2Translator getInstance() {
        if (translator == null) {
            translator = new GraphicC2Translator();
        }
        
        return translator;
    }
    
    /**
     * 去噪
     * 
     * @param picFile 图形验证码文件
     * @return
     * @throws Exception
     */
    private BufferedImage denoise(File picFile) throws Exception {
        BufferedImage img = ImageIO.read(picFile);
        int width = img.getWidth();
        int height = img.getHeight();
        final int TARGET = 0xff000099;
        for (int x = 0; x < width; ++x) {
            for (int y = 0; y < height; ++y) {
                if (img.getRGB(x, y) == TARGET) {
                    img.setRGB(x, y, TARGET_COLOR);
                } else {
                    img.setRGB(x, y, USELESS_COLOR);
                }
            }
        }
        return img;
    }

    /**
     * 分割元字符
     * 
     * @param img
     * @return
     * @throws Exception
     */
    private List<BufferedImage> split(BufferedImage img) throws Exception {
        List<BufferedImage> subImgs = new ArrayList<BufferedImage>();
        subImgs.add(img.getSubimage(4, 0, UNIT_W, UNIT_H));
        subImgs.add(img.getSubimage(16, 0, UNIT_W, UNIT_H));
        subImgs.add(img.getSubimage(28, 0, UNIT_W, UNIT_H));
        subImgs.add(img.getSubimage(40, 0, UNIT_W, UNIT_H));
        return subImgs;
    }

    /**
     * 取出训练数据
     * 
     * @return
     * @throws Exception
     */
    private BufferedImage loadTrainData() throws Exception {
        if (trainImg == null) {
            trainImg = ImageIO.read(this.getClass().getResourceAsStream("/resources/img/2/train.png"));
//            File file = new File(this.getClass().getResource("/resources/img/2/train.png").getPath());
//            File file = new File("E:/JavaWebProjects/SchTtable/reserve/成都医学院/ImageCode/train/train.png");
//            trainImg = ImageIO.read(file);
        }
        
        return trainImg;
    }

    /**
     * 将训练元字符装在一起
     * 
     * @param trainImg
     * @param smallImg
     * @param ch
     * @return
     */
    private boolean addTrainImg(BufferedImage trainImg, BufferedImage smallImg, char ch) {
        int which = Arrays.binarySearch(TRAIN_CHARS, ch);
        int x = -1;
        int y = -1;
        for (int i = 0; i < TRAIN_NUM; ++i) {
            if (trainImg.getRGB(i * UNIT_W, which * (UNIT_H + 1) + UNIT_H) != TARGET_COLOR) {
                x = i * UNIT_W;
                y = which * (UNIT_H + 1);
                break;
            }
        }
        
        if (x == -1 || y == -1) {
            return false;
        }
        
        for (int i = 0; i < UNIT_W; ++i) {
            for (int j = 0; j < UNIT_H; ++j) {
                trainImg.setRGB(x + i, y + j, smallImg.getRGB(i, j));
            }
        }
        trainImg.setRGB(x, y + UNIT_H, TARGET_COLOR);
        return true;
    }

    /**
     * 训练
     * 
     * @param rawDir
     * @param targetTrainFile
     * @return
     */
    public boolean train(File rawDir, File targetTrainFile) {
        try {
            BufferedImage trainImg = new BufferedImage(UNIT_W * TRAIN_NUM, (UNIT_H + 1) * TRAIN_CHARS.length, BufferedImage.TYPE_INT_ARGB);
            for (File file : rawDir.listFiles()) {
                BufferedImage img = denoise(file);
                List<BufferedImage> listImg = split(img);
                String[] parts = file.getName().split("\\.");
                char[] chars = parts[0].toCharArray();
                char[] addFlags;
                if (parts.length > 2) {
                    addFlags = parts[1].toCharArray();
                } else {
                    addFlags = new char[]{'1', '1', '1', '1'};
                }
                for (int i = 0, len = listImg.size(); i < len; ++i) {
                    if (addFlags[i] == '1') {
                        addTrainImg(trainImg, listImg.get(i), chars[i]);
                    }
                }
            }
            return ImageIO.write(trainImg, "PNG", targetTrainFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return false;
    }

    /**
     * 单元识别
     * 
     * @param img
     * @param trainImg
     * @return
     */
    private char recognize(BufferedImage img, BufferedImage trainImg) {
        char result = ' ';
        int width = img.getWidth();
        int height = img.getHeight();
        int min = width * height; // 最小差异像素数
        for (int i = 0; i < TRAIN_NUM; ++i) {
            for (int j = 0; j < TRAIN_CHARS.length; ++j) {
                int startX = UNIT_W * i;
                int startY = (UNIT_H + 1) * j;
                if (trainImg.getRGB(startX, startY + UNIT_H) != TARGET_COLOR) {
                    continue;
                }
                int count = 0; // 差异像素数
                for (int x = 0; x < UNIT_W; ++x) {
                    for (int y = 0; y < UNIT_H; ++y) {
                        count += (img.getRGB(x, y) != trainImg.getRGB(startX + x, startY + y) ? 1 : 0);
                        if (count >= min) {
                            break;
                        }
                    }
                }
                if (count < min) {
                    min = count;
                    result = TRAIN_CHARS[j];
                }
            }
        }
        
        return result;
    }

    /**
     * 识别
     * 
     * @param picFile 图形验证码文件
     * @return
     */
    public String translate(File picFile) {
        String result = "";
        try {
            BufferedImage img = denoise(picFile);
            List<BufferedImage> listImg = split(img);
            BufferedImage trainImg = loadTrainData();
            for (BufferedImage bi : listImg) {
                result += recognize(bi, trainImg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}