Je travaille sur la reconnaissance multi-chiffres manuscrite avec Java
, en utilisant la OpenCV
bibliothèque pour le prétraitement et la segmentation, et un Keras
modèle formé sur MNIST (avec une précision de 0,98) pour la reconnaissance.
La reconnaissance semble fonctionner assez bien, à part une chose. Le réseau ne reconnaît pas souvent ceux-là (numéro "un"). Je ne peux pas savoir si cela se produit en raison d'un prétraitement / d'une implémentation incorrecte de la segmentation, ou si un réseau formé sur le MNIST standard n'a tout simplement pas vu le numéro un qui ressemble à mes cas de test.
Voici à quoi ressemblent les chiffres problématiques après le prétraitement et la segmentation:
devient et est classé comme 4
.
devient et est classé comme 7
.
devient et est classé comme 4
. Etc...
Est-ce quelque chose qui pourrait être corrigé en améliorant le processus de segmentation? Ou plutôt en améliorant l'ensemble de formation?
Edit: Améliorer l'ensemble de formation (augmentation des données) aiderait certainement, ce que je teste déjà, la question du prétraitement correct reste toujours.
Mon prétraitement consiste à redimensionner, à convertir en niveaux de gris, à binariser, à inverser et à dilater. Voici le code:
Mat resized = new Mat();
Imgproc.resize(image, resized, new Size(), 8, 8, Imgproc.INTER_CUBIC);
Mat grayscale = new Mat();
Imgproc.cvtColor(resized, grayscale, Imgproc.COLOR_BGR2GRAY);
Mat binImg = new Mat(grayscale.size(), CvType.CV_8U);
Imgproc.threshold(grayscale, binImg, 0, 255, Imgproc.THRESH_OTSU);
Mat inverted = new Mat();
Core.bitwise_not(binImg, inverted);
Mat dilated = new Mat(inverted.size(), CvType.CV_8U);
int dilation_size = 5;
Mat kernel = Imgproc.getStructuringElement(Imgproc.CV_SHAPE_CROSS, new Size(dilation_size, dilation_size));
Imgproc.dilate(inverted, dilated, kernel, new Point(-1,-1), 1);
L'image prétraitée est ensuite segmentée en chiffres individuels comme suit:
List<Mat> digits = new ArrayList<>();
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(preprocessed.clone(), contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// code to sort contours
// code to check that contour is a valid char
List rects = new ArrayList<>();
for (MatOfPoint contour : contours) {
Rect boundingBox = Imgproc.boundingRect(contour);
Rect rectCrop = new Rect(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height);
rects.add(rectCrop);
}
for (int i = 0; i < rects.size(); i++) {
Rect x = (Rect) rects.get(i);
Mat digit = new Mat(preprocessed, x);
int border = 50;
Mat result = digit.clone();
Core.copyMakeBorder(result, result, border, border, border, border, Core.BORDER_CONSTANT, new Scalar(0, 0, 0));
Imgproc.resize(result, result, new Size(28, 28));
digits.add(result);
}