首先看看去幹擾線的結果(java)
原始圖檔
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc1TPB10dVhUZoRnMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DOycTOzEjM3EzMxkDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
去掉幹擾線以後的效果
這裡說下開發過程中遇到的問題
1.在網上使用了各種java類型的算法,直接對BufferedImage進行操作,但是都不理想
2.在使用Tesseract工具進行ocr識别的時候識别率也不高
解決第一個問題,我結合了網上的去幹擾線算法,以及使用了opencv算法。使用的opencv也是借鑒一篇網上的部落格。
解決第二個問題,是實用Tesseract工具針對我要識别的驗證碼進行獨立的訓練,而不是使用原始的訓練資料進行識别,這樣子可以明顯的提升識别率。
源碼
// 這裡是調用的核心方法
public class ImageCleanPlanOpencv implements ImageClean{
Logger logger = LoggerFactory.getLogger(ImageCleanPlanOpencv.class);
public BufferedImage clean(BufferedImage oriBufferedImage) {
try {
BufferedImage cleanedBufferedImage = null;
//這裡可以看到去燥的方法反複調用了幾次,是為了得更好的去幹擾線結果,這裡可以根據自己的驗證碼情況來編寫調用的次數,必須是偶數次,因為opencv的api會進行圖像反色
cleanedBufferedImage = cleanLinesInImage(oriBufferedImage);
cleanedBufferedImage=cleanLinesInImage(cleanedBufferedImage);
cleanedBufferedImage=cleanLinesInImage(cleanedBufferedImage);
cleanedBufferedImage=cleanLinesInImage(cleanedBufferedImage);
// try {
// ImageUtil.generateImage(cleanedBufferedImage, ImageConstant.url,"new_","");
// } catch (IOException e) {
// e.printStackTrace();
// }
return cleanedBufferedImage;
} catch (IOException e) {
logger.error("去噪過程異常",e);
e.printStackTrace();
}
return null;
}
/**
*
* @param oriBufferedImage 需要去噪的圖像
* @throws IOException
*/
public BufferedImage cleanLinesInImage(BufferedImage oriBufferedImage) throws IOException{
BufferedImage bufferedImage = oriBufferedImage;
int h = bufferedImage.getHeight();
int w = bufferedImage.getWidth();
// 灰階化
int[][] gray = new int[w][h];
for (int x = ; x < w; x++)
{
for (int y = ; y < h; y++)
{
int argb = bufferedImage.getRGB(x, y);
// 圖像加亮(調整亮度識别率非常高)
int r = (int) (((argb >> ) & ) * + );
int g = (int) (((argb >> ) & ) * + );
int b = (int) (((argb >> ) & ) * + );
if (r >= )
{
r = ;
}
if (g >= )
{
g = ;
}
if (b >= )
{
b = ;
}
gray[x][y] = (int) Math
.pow((Math.pow(r, ) * + Math.pow(g, )
* + Math.pow(b, ) * ), / );
}
}
// 二值化
int threshold = ostu(gray, w, h);
BufferedImage binaryBufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
for (int x = ; x < w; x++)
{
for (int y = ; y < h; y++)
{
if (gray[x][y] > threshold)
{
gray[x][y] |= ;
} else
{
gray[x][y] &= ;
}
binaryBufferedImage.setRGB(x, y, gray[x][y]);
}
}
//這裡開始是利用opencv的api進行處理
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat mat = bufferedImageToMat(binaryBufferedImage);
//第一次用opencv,這裡不太明白這個size對象如何使用,針對我要識别的驗證碼圖檔,調整為4,4的效果比較好,如果是不同的驗證碼圖檔,可以嘗試先不用這段opencv的代碼,或者微調這裡的參數
Mat kelner = Imgproc.getStructuringElement(MORPH_RECT, new Size(, ), new Point(-, -));
//腐蝕
Imgproc.erode(mat,mat,kelner);
//膨脹
Imgproc.dilate(mat,mat,kelner);
//圖像反色
Core.bitwise_not(mat,mat);
//去噪點
// Imgproc.morphologyEx(mat,mat, MORPH_OPEN, kelner,new Point(-1,-1),1);
binaryBufferedImage = mat2BufImg(mat,".png");
cleanImage(binaryBufferedImage,h,w );
return binaryBufferedImage;
}
public void cleanImage(BufferedImage binaryBufferedImage,int h ,int w ){
//去除幹擾線條
for(int y = ; y < h-; y++){
for(int x = ; x < w-; x++){
boolean flag = false ;
if(isBlack(binaryBufferedImage.getRGB(x, y))){
//左右均為空時,去掉此點
if(isWhite(binaryBufferedImage.getRGB(x-, y)) && isWhite(binaryBufferedImage.getRGB(x+, y))){
flag = true;
}
//上下均為空時,去掉此點
if(isWhite(binaryBufferedImage.getRGB(x, y+)) && isWhite(binaryBufferedImage.getRGB(x, y-))){
flag = true;
}
//斜上下為空時,去掉此點
if(isWhite(binaryBufferedImage.getRGB(x-, y+)) && isWhite(binaryBufferedImage.getRGB(x+, y-))){
flag = true;
}
if(isWhite(binaryBufferedImage.getRGB(x+, y+)) && isWhite(binaryBufferedImage.getRGB(x-, y-))){
flag = true;
}
if(flag){
binaryBufferedImage.setRGB(x,y,-);
}
}
}
}
}
public Mat bufferedImageToMat(BufferedImage bi) {
Mat mat = new Mat(bi.getHeight(), bi.getWidth(), CvType.CV_8UC1);
byte[] white = new byte[] { (byte) };
byte[] black = new byte[] { (byte) };
for (int x=; x<bi.getWidth(); x++) {
for (int y=; y<bi.getHeight(); y++) {
if (bi.getRGB(x, y) == Color.BLACK.getRGB()) {
mat.put(y, x, black);
} else {
mat.put(y, x, white);
}
}
}
return mat;
}
/**
* Mat轉換成BufferedImage
*
* @param matrix
* 要轉換的Mat
* @param fileExtension
* 格式為 ".jpg", ".png", etc
* @return
*/
public BufferedImage mat2BufImg (Mat matrix, String fileExtension) {
// convert the matrix into a matrix of bytes appropriate for
// this file extension
MatOfByte mob = new MatOfByte();
Imgcodecs.imencode(fileExtension, matrix, mob);
// convert the "matrix of bytes" into a byte array
byte[] byteArray = mob.toArray();
BufferedImage bufImage = null;
try {
InputStream in = new ByteArrayInputStream(byteArray);
bufImage = ImageIO.read(in);
} catch (Exception e) {
e.printStackTrace();
}
return bufImage;
}
public boolean isBlack(int colorInt)
{
Color color = new Color(colorInt);
if (color.getRed() + color.getGreen() + color.getBlue() <= )
{
return true;
}
return false;
}
public boolean isWhite(int colorInt)
{
Color color = new Color(colorInt);
if (color.getRed() + color.getGreen() + color.getBlue() > )
{
return true;
}
return false;
}
public int isBlackOrWhite(int colorInt)
{
if (getColorBright(colorInt) < || getColorBright(colorInt) > )
{
return ;
}
return ;
}
public int getColorBright(int colorInt)
{
Color color = new Color(colorInt);
return color.getRed() + color.getGreen() + color.getBlue();
}
public int ostu(int[][] gray, int w, int h)
{
int[] histData = new int[w * h];
// Calculate histogram
for (int x = ; x < w; x++)
{
for (int y = ; y < h; y++)
{
int red = & gray[x][y];
histData[red]++;
}
}
// Total number of pixels
int total = w * h;
float sum = ;
for (int t = ; t < ; t++)
sum += t * histData[t];
float sumB = ;
int wB = ;
int wF = ;
float varMax = ;
int threshold = ;
for (int t = ; t < ; t++)
{
wB += histData[t]; // Weight Background
if (wB == )
continue;
wF = total - wB; // Weight Foreground
if (wF == )
break;
sumB += (float) (t * histData[t]);
float mB = sumB / wB; // Mean Background
float mF = (sum - sumB) / wF; // Mean Foreground
// Calculate Between Class Variance
float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF);
// Check if new maximum found
if (varBetween > varMax)
{
varMax = varBetween;
threshold = t;
}
}
return threshold;
}
}
這段代碼除開opencv那段是我個人編寫的,其他的也是借鑒網上的源代碼。如果有編寫不對,或者可以修改的更好的建議請指出。
參考部落格
https://blog.csdn.net/firehood_/article/details/8433077
https://blog.csdn.net/shengfn/article/details/53582694
http://pinnau.blogspot.com/2016/06/java-create-opencv-mat-from.html
https://blog.csdn.net/qianmang/article/details/79158366