Sto lavorando al riconoscimento a più cifre stampato a mano con Java
, usando la OpenCV
libreria per la preelaborazione e la segmentazione, e un Keras
modello addestrato su MNIST (con una precisione di 0,98) per il riconoscimento.
Il riconoscimento sembra funzionare abbastanza bene, a parte una cosa. La rete abbastanza spesso non riesce a riconoscere quelli (numero "uno"). Non riesco a capire se ciò accade a causa della preelaborazione / implementazione errata della segmentazione o se una rete addestrata su MNIST standard non ha visto il numero uno che assomiglia ai miei casi di test.
Ecco come appaiono le cifre problematiche dopo la preelaborazione e la segmentazione:
diventa ed è classificato come 4
.
diventa ed è classificato come 7
.
diventa ed è classificato come 4
. E così via...
È qualcosa che potrebbe essere risolto migliorando il processo di segmentazione? O meglio migliorando il set di allenamento?
Modifica: migliorare il set di addestramento (aumento dei dati) sarebbe sicuramente di aiuto, cosa che sto già testando, la questione della corretta preelaborazione rimane ancora.
La mia preelaborazione consiste nel ridimensionamento, conversione in scala di grigi, binarizzazione, inversione e dilatazione. Ecco il codice:
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'immagine preelaborata viene quindi segmentata in singole cifre come segue:
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);
}