Pracuję nad odręcznym rozpoznawaniem wielocyfrowym przy Java
użyciu OpenCV
biblioteki do wstępnego przetwarzania i segmentacji oraz Keras
modelu wyszkolonego w zakresie MNIST (z dokładnością 0,98) do rozpoznawania.
Rozpoznawanie wydaje się działać całkiem dobrze, pomijając jedną rzecz. Sieć dość często nie rozpoznaje tych (numer „jeden”). Nie mogę ustalić, czy dzieje się tak z powodu wstępnego przetwarzania / nieprawidłowej implementacji segmentacji, czy też sieć wyszkolona na standardowym MNIST po prostu nie widziała numeru jeden, który wygląda jak moje przypadki testowe.
Oto jak wyglądają problematyczne cyfry po przetwarzaniu wstępnym i segmentacji:
staje się i jest klasyfikowany jako 4
.
staje się i jest klasyfikowany jako 7
.
staje się i jest klasyfikowany jako 4
. I tak dalej...
Czy można to naprawić, poprawiając proces segmentacji? Czy raczej poprzez ulepszenie zestawu treningowego?
Edycja: Ulepszenie zestawu treningowego (augmentacja danych) zdecydowanie by pomogło, co już testuję, wciąż pozostaje kwestia poprawnego przetwarzania wstępnego.
Moje wstępne przetwarzanie obejmuje zmianę rozmiaru, konwersję do skali szarości, binaryzację, inwersję i rozszerzenie. Oto kod:
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);
Wstępnie przetworzony obraz jest następnie dzielony na poszczególne cyfry w następujący sposób:
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);
}