Saya bekerja pada pengenalan multi-digit yang dicetak dengan tangan Java
, menggunakan OpenCV
perpustakaan untuk preprocessing dan segmentasi, dan Keras
model yang dilatih tentang MNIST (dengan akurasi 0,98) untuk pengakuan.
Pengakuan itu tampaknya bekerja cukup baik, terlepas dari satu hal. Jaringan sering gagal mengenali yang (nomor "satu"). Saya tidak tahu apakah itu terjadi karena preprocessing / implementasi segmentasi yang tidak benar, atau jika jaringan yang dilatih tentang MNIST standar belum melihat nomor satu yang terlihat seperti kasus pengujian saya.
Inilah tampilan digit bermasalah setelah preprocessing dan segmentasi:
menjadi dan diklasifikasikan sebagai 4
.
menjadi dan diklasifikasikan sebagai 7
.
menjadi dan diklasifikasikan sebagai 4
. Dan seterusnya...
Apakah ini sesuatu yang bisa diperbaiki dengan memperbaiki proses segmentasi? Atau lebih tepatnya dengan meningkatkan set pelatihan?
Sunting: Meningkatkan set pelatihan (augmentasi data) pasti akan membantu, yang sudah saya uji, pertanyaan tentang preprocessing yang benar masih ada.
Preprocessing saya terdiri dari pengubahan ukuran, konversi ke skala abu-abu, binarisasi, inversi, dan pelebaran. Berikut kodenya:
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);
Gambar yang telah diproses kemudian dibagi menjadi beberapa digit sebagai berikut:
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);
}
sumber
Jawaban:
Saya percaya bahwa masalah Anda adalah proses pelebaran. Saya mengerti bahwa Anda ingin menormalkan ukuran gambar, tetapi Anda tidak boleh mematahkan proporsi, Anda harus mengubah ukuran hingga maksimum yang diinginkan oleh satu sumbu (yang memungkinkan skala ulang terbesar tanpa membiarkan dimensi sumbu lain melebihi ukuran maksimum) dan mengisi dengan warna latar belakang sisa gambar. Bukannya "MNIST standar hanya belum melihat nomor satu yang terlihat seperti kasus pengujian Anda", Anda membuat gambar Anda terlihat seperti nomor terlatih yang berbeda (yang diakui)
Jika Anda mempertahankan rasio aspek gambar yang benar (sumber dan pasca-pemrosesan), Anda dapat melihat bahwa Anda tidak hanya mengubah ukuran gambar tetapi "mengubah" gambar. Ini bisa merupakan hasil dari pelebaran non-homogen atau pengubahan ukuran yang salah
sumber
Imgproc.resize(image, resized, new Size(), 8, 8, Imgproc.INTER_CUBIC);
. Di sini aspek rasio tetap sama, di mana saya harus mematahkan proporsi?Sudah ada beberapa jawaban yang diposting tetapi tidak satupun dari mereka menjawab pertanyaan Anda yang sebenarnya tentang preprocessing gambar .
Pada gilirannya saya tidak melihat masalah signifikan dengan implementasi Anda selama ini adalah proyek studi, dilakukan dengan baik.
Tapi satu hal yang perlu diperhatikan yang mungkin Anda lewatkan. Ada operasi dasar dalam morfologi matematika: erosi dan pelebaran (digunakan oleh Anda). Dan ada operasi yang kompleks: berbagai kombinasi yang dasar (mis. Pembukaan dan penutupan). Tautan Wikipedia bukan referensi CV terbaik, tetapi Anda bisa mulai dengan itu untuk mendapatkan ide.
Biasanya lebih baik menggunakan pembukaan daripada erosi dan penutupan bukan pelebaran karena dalam hal ini gambar biner asli berubah jauh lebih sedikit (tetapi efek yang diinginkan dari membersihkan tepi tajam atau mengisi celah tercapai). Jadi dalam kasus Anda, Anda harus memeriksa penutupan (pelebaran gambar diikuti oleh erosi dengan kernel yang sama). Dalam kasus gambar ekstra-kecil 8 * 8 sangat dimodifikasi ketika Anda melebar bahkan dengan kernel 1 * 1 (1 piksel lebih dari 16% gambar) yang kurang pada gambar yang lebih besar).
Untuk memvisualisasikan ide, lihat gambar berikut (dari tutorial OpenCV: 1 , 2 ):
pelebaran:
penutupan:
Semoga ini bisa membantu.
sumber
Jadi, Anda memerlukan pendekatan yang kompleks karena setiap langkah kaskade komputasi Anda berdasarkan hasil sebelumnya. Dalam algoritme Anda, Anda memiliki fitur berikut:
Seperti disebutkan sebelumnya, jika Anda menerapkan pengubahan ukuran, maka Anda kehilangan informasi tentang rasio aspek gambar. Anda harus melakukan pemrosesan ulang gambar digit yang sama untuk mendapatkan hasil yang sama dengan yang tersirat dalam proses pelatihan.
Cara yang lebih baik jika Anda hanya memotong gambar dengan gambar ukuran tetap. Dalam varian itu Anda tidak perlu mencari dan mengubah ukuran gambar digit kontur sebelum proses pelatihan. Kemudian, Anda dapat membuat sedikit perubahan pada algoritme krop Anda untuk mengenali lebih baik: mudah menemukan kontur dan letakkan digit Anda tanpa mengubah ukuran di tengah bingkai gambar yang relevan untuk pengakuan.
Anda juga harus lebih memperhatikan algoritma binarisasi. Saya memiliki pengalaman mempelajari efek nilai ambang binarisasi pada kesalahan belajar: Saya dapat mengatakan bahwa ini adalah faktor yang sangat signifikan. Anda dapat mencoba algoritma binarisasi lainnya untuk memeriksa ide ini. Misalnya, Anda dapat menggunakan perpustakaan ini untuk menguji algoritma binarisasi alternatif.
Untuk meningkatkan kualitas pengakuan Anda menggunakan validasi silang pada proses pelatihan. Ini membantu Anda menghindari masalah overfitting untuk data pelatihan Anda. Misalnya Anda dapat membaca artikel ini di mana dijelaskan bagaimana menggunakannya dengan Keras.
Kadang-kadang tingkat akurasi yang lebih tinggi tidak mengatakan apa-apa tentang kualitas pengakuan yang sebenarnya karena JST yang terlatih tidak menemukan pola dalam data pelatihan. Ini mungkin terhubung dengan proses pelatihan atau dataset input seperti yang dijelaskan di atas, atau mungkin disebabkan oleh arsitektur JST pilih.
Ini masalah besar. Bagaimana cara mendefinisikan arsitektur JST yang lebih baik untuk menyelesaikan tugas? Tidak ada cara umum untuk melakukan hal itu. Tetapi ada beberapa cara untuk lebih mendekati ideal. Misalnya Anda bisa membaca buku ini . Ini membantu Anda untuk membuat visi yang lebih baik untuk masalah Anda. Anda juga dapat menemukan di sini beberapa rumus heuristik yang sesuai dengan jumlah lapisan / elemen tersembunyi untuk JST Anda. Juga di sini Anda akan menemukan sedikit gambaran untuk ini.
Saya harap ini akan membantu.
sumber
Setelah beberapa penelitian dan percobaan, saya sampai pada kesimpulan bahwa preprocessing gambar itu sendiri bukan masalah (saya memang mengubah beberapa parameter yang disarankan, seperti misalnya ukuran dan bentuk pelebaran tetapi mereka tidak penting untuk hasil). Namun, apa yang membantu adalah 2 hal berikut:
Seperti @ f4f perhatikan, saya perlu mengumpulkan dataset saya sendiri dengan data dunia nyata. Ini sudah sangat membantu.
Saya membuat perubahan penting pada preprocessing segmentasi saya. Setelah mendapatkan kontur individual, pertama-tama saya menormalkan ukuran gambar agar pas dengan
20x20
kotak pixel (seperti yang ada di dalamnyaMNIST
). Setelah itu saya memusatkan kotak di tengah28x28
gambar menggunakan pusat massa (yang untuk gambar biner adalah nilai rata-rata di kedua dimensi).Tentu saja, masih ada kasus segmentasi yang sulit, seperti angka yang tumpang tindih atau terhubung, tetapi perubahan di atas menjawab pertanyaan awal saya dan meningkatkan kinerja klasifikasi saya.
sumber