From 16a42af437883e4b1a8a434b8ba38df8fc1a60bf Mon Sep 17 00:00:00 2001
From: Enliven26 <16521443@mahasiswa.itb.ac.id>
Date: Sun, 19 May 2024 12:26:51 +0700
Subject: [PATCH] refactor: javadocs

---
 .../data/configs/omr/CircleTemplateLoader.kt  |  9 +++
 .../configs/omr/ContourOMRHelperConfig.kt     | 10 +++
 .../omrekap/data/configs/omr/OMRCropper.kt    | 14 ++++
 .../data/configs/omr/OMRCropperConfig.kt      | 15 ++++
 .../data/configs/omr/OMRHelperConfig.kt       |  4 ++
 .../omrekap/data/configs/omr/OMRSection.kt    |  3 +
 .../omr/TemplateMatchingOMRHelperConfig.kt    |  6 ++
 .../k2_9/omrekap/data/models/CornerPoints.kt  |  7 ++
 .../k2_9/omrekap/data/models/ImageSaveData.kt |  7 ++
 .../data/models/OMRBaseConfiguration.kt       |  6 ++
 .../data/repository/OMRConfigRepository.kt    | 17 +++++
 .../data/repository/OMRJsonConfigLoader.kt    | 13 ++++
 .../data/view_models/ImageDataViewModel.kt    |  8 +++
 .../data/view_models/PreviewViewModel.kt      |  7 ++
 .../com/k2_9/omrekap/utils/AprilTagHelper.kt  | 15 ++++
 .../java/com/k2_9/omrekap/utils/CropHelper.kt | 18 +++++
 .../omrekap/utils/ImageAnnotationHelper.kt    | 37 ++++++++++
 .../k2_9/omrekap/utils/ImageSaveDataHolder.kt | 11 +++
 .../k2_9/omrekap/utils/PermissionHelper.kt    | 10 +++
 .../k2_9/omrekap/utils/PreprocessHelper.kt    |  8 +++
 .../java/com/k2_9/omrekap/utils/SaveHelper.kt | 32 +++++++++
 .../omrekap/utils/omr/ContourOMRHelper.kt     | 70 ++++++++++++++++++-
 .../omrekap/utils/omr/OMRConfigDetector.kt    |  5 ++
 .../com/k2_9/omrekap/utils/omr/OMRHelper.kt   | 43 ++++++++++++
 .../utils/omr/TemplateMatchingOMRHelper.kt    | 19 +++++
 25 files changed, 392 insertions(+), 2 deletions(-)

diff --git a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/CircleTemplateLoader.kt b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/CircleTemplateLoader.kt
index f2deec7..c1bac3f 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/CircleTemplateLoader.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/CircleTemplateLoader.kt
@@ -6,7 +6,16 @@ import org.opencv.core.MatOfByte
 import org.opencv.imgcodecs.Imgcodecs
 import java.io.InputStream
 
+/**
+ * Load the circle template image
+ * @param appContext application context
+ * @param resId resource id of the circle template image
+ */
 class CircleTemplateLoader(private val appContext: Context, private val resId: Int) {
+	/**
+	 * Load the template image
+	 * @return template image
+	 */
 	fun loadTemplateImage(): Mat {
 		val inputStream: InputStream = appContext.resources.openRawResource(resId)
 		val byteArray = inputStream.readBytes()
diff --git a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/ContourOMRHelperConfig.kt b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/ContourOMRHelperConfig.kt
index 4053bca..911d92d 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/ContourOMRHelperConfig.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/ContourOMRHelperConfig.kt
@@ -1,5 +1,15 @@
 package com.k2_9.omrekap.data.configs.omr
 
+/**
+ * Configuration for the OMR helper
+ * @param omrCropper cropper for the OMR section
+ * @param minRadius minimum radius of the circle
+ * @param maxRadius maximum radius of the circle
+ * @param minAspectRatio minimum aspect ratio of the circle
+ * @param maxAspectRatio maximum aspect ratio of the circle
+ * @param darkPercentageThreshold threshold for the percentage of dark pixels in the circle
+ * @param darkIntensityThreshold threshold for the intensity of dark pixels in the circle
+ */
 class ContourOMRHelperConfig(
 	omrCropper: OMRCropper,
 	minRadius: Int,
diff --git a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRCropper.kt b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRCropper.kt
index 7ee53ff..66f5e63 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRCropper.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRCropper.kt
@@ -3,7 +3,16 @@ package com.k2_9.omrekap.data.configs.omr
 import org.opencv.core.Mat
 import org.opencv.core.Rect
 
+/**
+ * Cropper for OMR section
+ * @param config configuration for the cropper
+ */
 class OMRCropper(val config: OMRCropperConfig) {
+	/**
+	 * Crop the image to the section
+	 * @param section section to be cropped
+	 * @return cropped image
+	 */
 	fun crop(section: OMRSection): Mat {
 		val (x, y) = config.getSectionPosition(section)
 		val (width, height) = config.omrSectionSize
@@ -13,6 +22,11 @@ class OMRCropper(val config: OMRCropperConfig) {
 		return Mat(config.image, roi)
 	}
 
+	/**
+	 * Get the position of the section
+	 * @param section section to get the position
+	 * @return position of the section
+	 */
 	fun sectionPosition(section: OMRSection): Rect {
 		val (x, y) = config.getSectionPosition(section)
 		val (width, height) = config.omrSectionSize
diff --git a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRCropperConfig.kt b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRCropperConfig.kt
index fabe926..9a9be96 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRCropperConfig.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRCropperConfig.kt
@@ -2,6 +2,12 @@ package com.k2_9.omrekap.data.configs.omr
 
 import org.opencv.core.Mat
 
+/**
+ * Configuration for the cropper
+ * @param image image to be cropped
+ * @param omrSectionSize size of the OMR section
+ * @param omrSectionPosition position of the OMR section
+ */
 class OMRCropperConfig(
 	image: Mat?,
 	val omrSectionSize: Pair<Int, Int>,
@@ -39,10 +45,19 @@ class OMRCropperConfig(
 		}
 	}
 
+	/**
+	 * Get the position of the section
+	 * @param section section to get the position
+	 * @return position of the section
+	 */
 	fun getSectionPosition(section: OMRSection): Pair<Int, Int> {
 		return omrSectionPosition[section]!!
 	}
 
+	/**
+	 * Set the image
+	 * @param image image to be set
+	 */
 	fun setImage(image: Mat) {
 		require(omrSectionSize.first <= image.width() && omrSectionSize.second <= image.height()) {
 			"OMR section size must be less than or equal to the image size"
diff --git a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRHelperConfig.kt b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRHelperConfig.kt
index 0022677..8a72269 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRHelperConfig.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRHelperConfig.kt
@@ -1,5 +1,9 @@
 package com.k2_9.omrekap.data.configs.omr
 
+/**
+ * Configuration for the OMR helper
+ * @param omrCropper cropper for the OMR section
+ */
 open class OMRHelperConfig(
 	val omrCropper: OMRCropper,
 )
diff --git a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRSection.kt b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRSection.kt
index d6f0ce8..3fc6350 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRSection.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/OMRSection.kt
@@ -1,5 +1,8 @@
 package com.k2_9.omrekap.data.configs.omr
 
+/**
+ * Enum for OMR section
+ */
 enum class OMRSection {
 	FIRST,
 	SECOND,
diff --git a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/TemplateMatchingOMRHelperConfig.kt b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/TemplateMatchingOMRHelperConfig.kt
index f3d83c3..59d62fb 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/configs/omr/TemplateMatchingOMRHelperConfig.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/configs/omr/TemplateMatchingOMRHelperConfig.kt
@@ -2,6 +2,12 @@ package com.k2_9.omrekap.data.configs.omr
 
 import org.opencv.core.Mat
 
+/**
+ * Configuration for the OMR helper using template matching
+ * @param omrCropper cropper for the OMR section
+ * @param templateLoader loader for the template image
+ * @param similarityThreshold threshold for the similarity between the template and the cropped image
+ */
 class TemplateMatchingOMRHelperConfig(
 	omrCropper: OMRCropper,
 	templateLoader: CircleTemplateLoader?,
diff --git a/app/src/main/java/com/k2_9/omrekap/data/models/CornerPoints.kt b/app/src/main/java/com/k2_9/omrekap/data/models/CornerPoints.kt
index e3f8f5c..af17fa9 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/models/CornerPoints.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/models/CornerPoints.kt
@@ -2,6 +2,13 @@ package com.k2_9.omrekap.data.models
 
 import org.opencv.core.Point
 
+/**
+ * Data class for corner points of a document
+ * @param topLeft top left corner point
+ * @param topRight top right corner point
+ * @param bottomRight bottom right corner point
+ * @param bottomLeft bottom left corner point
+ */
 data class CornerPoints(
 	val topLeft: Point,
 	val topRight: Point,
diff --git a/app/src/main/java/com/k2_9/omrekap/data/models/ImageSaveData.kt b/app/src/main/java/com/k2_9/omrekap/data/models/ImageSaveData.kt
index e8186b4..0002067 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/models/ImageSaveData.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/models/ImageSaveData.kt
@@ -3,6 +3,13 @@ package com.k2_9.omrekap.data.models
 import android.graphics.Bitmap
 import java.time.Instant
 
+/**
+ * Data class for saving image data
+ * @param rawImage raw image
+ * @param annotatedImage image with annotations
+ * @param data map of candidate names and their vote counts
+ * @param timestamp timestamp of the image
+ */
 data class ImageSaveData(
 	val rawImage: Bitmap,
 	var annotatedImage: Bitmap,
diff --git a/app/src/main/java/com/k2_9/omrekap/data/models/OMRBaseConfiguration.kt b/app/src/main/java/com/k2_9/omrekap/data/models/OMRBaseConfiguration.kt
index f5cc6f3..040dab3 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/models/OMRBaseConfiguration.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/models/OMRBaseConfiguration.kt
@@ -11,6 +11,12 @@ data class OMRBaseConfiguration(
 	val omrConfigs: Map<String, OMRConfigurationParameter>,
 )
 
+/**
+ * Configuration parameters for OMR detection
+ * @param contents map of OMR section and candidate name
+ * @param contourOMRHelperConfig configuration for contour-based OMR detection
+ * @param templateMatchingOMRHelperConfig configuration for template matching-based OMR detection
+ */
 data class OMRConfigurationParameter(
 	val contents: Map<OMRSection, String>,
 	val contourOMRHelperConfig: ContourOMRHelperConfig,
diff --git a/app/src/main/java/com/k2_9/omrekap/data/repository/OMRConfigRepository.kt b/app/src/main/java/com/k2_9/omrekap/data/repository/OMRConfigRepository.kt
index 05dbebd..94433d8 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/repository/OMRConfigRepository.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/repository/OMRConfigRepository.kt
@@ -8,7 +8,14 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import java.io.IOException
 
+/** Repository for loading OMR configurations from JSON */
+
 object OMRConfigRepository {
+	/**
+	 * Load OMR configurations from JSON file
+	 * @param context application context
+	 * @return OMRBaseConfiguration object
+	 */
 	suspend fun loadConfigurations(context: Context): OMRBaseConfiguration? {
 		val jsonString =
 			withContext(Dispatchers.IO) {
@@ -23,12 +30,22 @@ object OMRConfigRepository {
 		}
 	}
 
+	/**
+	 * Get the JSON string of the OMR configuration
+	 * @param omrBaseConfiguration OMR configuration object
+	 * @return JSON string of the configuration
+	 */
 	fun printConfigurationJson(omrBaseConfiguration: OMRBaseConfiguration): String {
 		val jsonString = OMRJsonConfigLoader.toJson(omrBaseConfiguration)
 		Log.d("JSONConfigRepo", jsonString)
 		return jsonString
 	}
 
+	/**
+	 * Read the JSON configuration file
+	 * @param context application context
+	 * @return JSON string of the configuration
+	 */
 	private fun readConfigString(context: Context): String? {
 		val inputStream = context.assets.open("omr_config.json")
 		return try {
diff --git a/app/src/main/java/com/k2_9/omrekap/data/repository/OMRJsonConfigLoader.kt b/app/src/main/java/com/k2_9/omrekap/data/repository/OMRJsonConfigLoader.kt
index 9cf3d98..2a3d64f 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/repository/OMRJsonConfigLoader.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/repository/OMRJsonConfigLoader.kt
@@ -3,13 +3,26 @@ package com.k2_9.omrekap.data.repository
 import com.google.gson.Gson
 import com.k2_9.omrekap.data.models.OMRBaseConfiguration
 
+/**
+ * JSON configuration loader for OMR configurations
+ */
 object OMRJsonConfigLoader {
 	private val gson = Gson()
 
+	/**
+	 * Parse JSON string to OMRBaseConfiguration object
+	 * @param jsonString JSON string
+	 * @return OMRBaseConfiguration object
+	 */
 	fun parseJson(jsonString: String): OMRBaseConfiguration? {
 		return gson.fromJson(jsonString, OMRBaseConfiguration::class.java)
 	}
 
+	/**
+	 * Convert OMRBaseConfiguration object to JSON string
+	 * @param omrBaseConfiguration OMRBaseConfiguration object
+	 * @return JSON string
+	 */
 	fun toJson(omrBaseConfiguration: OMRBaseConfiguration): String {
 		return gson.toJson(omrBaseConfiguration)
 	}
diff --git a/app/src/main/java/com/k2_9/omrekap/data/view_models/ImageDataViewModel.kt b/app/src/main/java/com/k2_9/omrekap/data/view_models/ImageDataViewModel.kt
index 3f3696f..1ba7b58 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/view_models/ImageDataViewModel.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/view_models/ImageDataViewModel.kt
@@ -21,10 +21,18 @@ import org.opencv.core.Mat
 import org.opencv.imgproc.Imgproc
 import java.time.Instant
 
+/**
+ * ViewModel for image data processing in Result page
+ */
 class ImageDataViewModel : ViewModel() {
 	private val _data = MutableLiveData<ImageSaveData?>()
 	val data = _data as LiveData<ImageSaveData?>
 
+	/**
+	 * Process the image data with OMR detection
+	 * @param data image data to be processed
+	 * @param circleTemplateLoader loader for circle template
+	 */
 	fun processImage(
 		data: ImageSaveData,
 		circleTemplateLoader: CircleTemplateLoader,
diff --git a/app/src/main/java/com/k2_9/omrekap/data/view_models/PreviewViewModel.kt b/app/src/main/java/com/k2_9/omrekap/data/view_models/PreviewViewModel.kt
index 89451f9..6176644 100644
--- a/app/src/main/java/com/k2_9/omrekap/data/view_models/PreviewViewModel.kt
+++ b/app/src/main/java/com/k2_9/omrekap/data/view_models/PreviewViewModel.kt
@@ -10,10 +10,17 @@ import com.k2_9.omrekap.utils.PreprocessHelper
 import kotlinx.coroutines.launch
 import java.time.Instant
 
+/**
+ * ViewModel for image data processing in Preview page
+ */
 class PreviewViewModel : ViewModel() {
 	private val _data = MutableLiveData<ImageSaveData>()
 	val data = _data as LiveData<ImageSaveData>
 
+	/**
+	 * Preprocess the image data
+	 * @param img image to be preprocessed
+	 */
 	fun preprocessImage(img: Bitmap) {
 		viewModelScope.launch {
 			val data = ImageSaveData(img, img, mapOf(), Instant.now())
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/AprilTagHelper.kt b/app/src/main/java/com/k2_9/omrekap/utils/AprilTagHelper.kt
index ecd7aac..7a61a3b 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/AprilTagHelper.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/AprilTagHelper.kt
@@ -74,6 +74,11 @@ object AprilTagHelper {
 		return (idList to corners)
 	}
 
+	/**
+	 * Annotate the detected april tag and its ID to the image
+	 * @param imageBitmap image to be annotated
+	 * @return annotated image in OpenCV's Mat type
+	 */
 	fun annotateImage(imageBitmap: Bitmap): Mat {
 		val res = getAprilTagId(imageBitmap)
 		val cornerPoints = res.second
@@ -81,6 +86,11 @@ object AprilTagHelper {
 		return ImageAnnotationHelper.annotateAprilTag(prepareImage(imageBitmap), cornerPoints, ids)
 	}
 
+	/**
+	 * Prepare detector with given dictionary
+	 * @param detectorDictionary dictionary to be used for detection
+	 * @return prepared ArucoDetector
+	 */
 	private fun prepareDetector(detectorDictionary: Dictionary): ArucoDetector {
 		// initialize detector parameters
 		val detectorParameters = DetectorParameters()
@@ -88,6 +98,11 @@ object AprilTagHelper {
 		return ArucoDetector(detectorDictionary, detectorParameters)
 	}
 
+	/**
+	 * Prepare image for ArucoDetector with preprocessing
+	 * @param imageBitmap image to be prepared
+	 * @return prepared image in OpenCV's Mat type
+	 */
 	private fun prepareImage(imageBitmap: Bitmap): Mat {
 		// transform to OpenCV Mat data
 		val imageMat = Mat()
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/CropHelper.kt b/app/src/main/java/com/k2_9/omrekap/utils/CropHelper.kt
index 1ff85ab..03324a2 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/CropHelper.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/CropHelper.kt
@@ -16,6 +16,9 @@ import org.opencv.imgproc.Imgproc.warpPerspective
 import kotlin.math.pow
 import kotlin.math.sqrt
 
+/**
+ * Helper class for cropping image based on corner points detection
+ */
 object CropHelper {
 	private const val UPPER_LEFT: Int = 0
 	private const val UPPER_RIGHT: Int = 1
@@ -24,6 +27,10 @@ object CropHelper {
 
 	private lateinit var pattern: Mat
 
+	/**
+	 * Load corner pattern image
+	 * @param patternBitmap pattern image in Bitmap
+	 */
 	fun loadPattern(patternBitmap: Bitmap) {
 		// Load only if pattern hasn't been loaded
 		if (::pattern.isInitialized) return
@@ -36,6 +43,11 @@ object CropHelper {
 		this.pattern = PreprocessHelper.preprocessPattern(this.pattern)
 	}
 
+	/**
+	 * Detect corner points in the image
+	 * @param img image to be processed
+	 * @return corner points
+	 */
 	fun detectCorner(img: Mat): CornerPoints {
 		// If pattern hasn't been loaded, throw exception
 		if (!::pattern.isInitialized) {
@@ -117,6 +129,12 @@ object CropHelper {
 		return CornerPoints(upperLeftPoint, upperRightPoint, lowerRightPoint, lowerLeftPoint)
 	}
 
+	/**
+	 * Transform image based on corner points into a rectangle
+	 * @param img image to be processed
+	 * @param points corner points of the image
+	 * @return tilted corrected image
+	 */
 	fun fourPointTransform(
 		img: Mat,
 		points: CornerPoints,
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/ImageAnnotationHelper.kt b/app/src/main/java/com/k2_9/omrekap/utils/ImageAnnotationHelper.kt
index 7ae0919..5aef245 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/ImageAnnotationHelper.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/ImageAnnotationHelper.kt
@@ -8,7 +8,16 @@ import org.opencv.core.Rect
 import org.opencv.core.Scalar
 import org.opencv.imgproc.Imgproc
 
+/**
+ * Helper class for annotating image with detected objects
+ */
 object ImageAnnotationHelper {
+	/**
+	 * Annotate the corner points of a document
+	 * @param img image to be annotated
+	 * @param cornerPoints corner points of the document
+	 * @return image with annotations
+	 */
 	fun annotateCorner(
 		img: Mat,
 		cornerPoints: CornerPoints,
@@ -21,6 +30,13 @@ object ImageAnnotationHelper {
 		return imgWithAnnotations
 	}
 
+	/**
+	 * Annotate the detected AprilTag
+	 * @param img image to be annotated
+	 * @param cornerPoints corner points of the AprilTag
+	 * @param id ID of the AprilTag
+	 * @return image with annotations
+	 */
 	fun annotateAprilTag(
 		img: Mat,
 		cornerPoints: List<Mat>,
@@ -76,6 +92,13 @@ object ImageAnnotationHelper {
 		return imgWithAnnotations
 	}
 
+	/**
+	 * Annotate the detected vote count in the image gained from template matching
+	 * @param img image to be annotated
+	 * @param cornerPoints corner points of the detected object
+	 * @param contourNumber number of the detected object
+	 * @return image with annotations
+	 */
 	fun annotateTemplateMatchingOMR(
 		img: Mat,
 		cornerPoints: List<Rect>,
@@ -99,6 +122,13 @@ object ImageAnnotationHelper {
 		return imgWithAnnotations
 	}
 
+	/**
+	 * Annotate the detected vote count in the image gained from contour detection
+	 * @param img image to be annotated
+	 * @param cornerPoints corner points of the detected object
+	 * @param contourNumber number of the detected object
+	 * @return image with annotations
+	 */
 	fun annotateContourOMR(
 		img: Mat,
 		cornerPoints: List<MatOfPoint>,
@@ -121,6 +151,13 @@ object ImageAnnotationHelper {
 		return imgWithAnnotations
 	}
 
+	/**
+	 * Annotate the detected OMR result in the image
+	 * @param img image to be annotated
+	 * @param section section of the detected OMR
+	 * @param result result of the detected OMR
+	 * @return image with annotations
+	 */
 	fun annotateOMR(
 		img: Mat,
 		section: Rect,
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/ImageSaveDataHolder.kt b/app/src/main/java/com/k2_9/omrekap/utils/ImageSaveDataHolder.kt
index 2f39677..2e6590e 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/ImageSaveDataHolder.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/ImageSaveDataHolder.kt
@@ -3,13 +3,24 @@ package com.k2_9.omrekap.utils
 import android.util.Log
 import com.k2_9.omrekap.data.models.ImageSaveData
 
+/**
+ * Decorator for ImageSaveData
+ */
 object ImageSaveDataHolder {
 	private var imageSaveData: ImageSaveData? = null
 
+	/**
+	 * Set the ImageSaveData
+	 * @param data ImageSaveData to be saved
+	 */
 	fun save(data: ImageSaveData) {
 		imageSaveData = data
 	}
 
+	/**
+	 * Get the ImageSaveData
+	 * @return ImageSaveData
+	 */
 	fun get(): ImageSaveData {
 		if (imageSaveData == null) {
 			Log.e("ImageSaveDataHolder", "ImageSaveData is null")
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/PermissionHelper.kt b/app/src/main/java/com/k2_9/omrekap/utils/PermissionHelper.kt
index 70e9154..32384e6 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/PermissionHelper.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/PermissionHelper.kt
@@ -6,7 +6,17 @@ import androidx.activity.result.contract.ActivityResultContracts
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.ContextCompat
 
+/**
+ * Helper class for handling permissions
+ */
 object PermissionHelper {
+	/**
+	 * Request permission from the user
+	 * @param activity activity context
+	 * @param permission permission to be requested
+	 * @param verbose show toast message if permission is denied
+	 * @param operation operation to be executed if permission is granted
+	 */
 	fun requirePermission(
 		activity: AppCompatActivity,
 		permission: String,
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/PreprocessHelper.kt b/app/src/main/java/com/k2_9/omrekap/utils/PreprocessHelper.kt
index 6858fea..cef1316 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/PreprocessHelper.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/PreprocessHelper.kt
@@ -9,10 +9,18 @@ import org.opencv.core.Size
 import org.opencv.imgproc.Imgproc
 import java.time.Instant
 
+/**
+ * Helper class for preprocessing image
+ */
 object PreprocessHelper {
 	private const val FINAL_WIDTH = 900.0
 	private const val FINAL_HEIGHT = 1600.0
 
+	/**
+	 * Preprocess the image data
+	 * @param data image data to be preprocessed
+	 * @return preprocessed image data
+	 */
 	fun preprocessImage(data: ImageSaveData): ImageSaveData {
 		// Initialize Mats
 		val mainImageMat = Mat()
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/SaveHelper.kt b/app/src/main/java/com/k2_9/omrekap/utils/SaveHelper.kt
index 793eec7..e612364 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/SaveHelper.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/SaveHelper.kt
@@ -19,7 +19,15 @@ import java.text.SimpleDateFormat
 import java.util.Date
 import java.util.Locale
 
+/**
+ * Helper class for saving images and JSON data
+ */
 object SaveHelper {
+	/**
+	 * Save the image and JSON data to the device
+	 * @param context the application context
+	 * @param data the image and JSON data to be saved
+	 */
 	suspend fun save(
 		context: Context,
 		data: ImageSaveData,
@@ -48,6 +56,12 @@ object SaveHelper {
 		}
 	}
 
+	/**
+	 * Convert the selected file URI to a Bitmap
+	 * @param context the application context
+	 * @param selectedFileUri the URI of the selected file
+	 * @return the converted Bitmap
+	 */
 	fun uriToBitmap(
 		context: Context,
 		selectedFileUri: Uri,
@@ -59,11 +73,22 @@ object SaveHelper {
 		return image
 	}
 
+	/**
+	 * Generate a folder name based on the current date and time
+	 * @return the generated folder name
+	 */
 	private fun generateFolderName(): String {
 		val sdf = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault())
 		return sdf.format(Date())
 	}
 
+	/**
+	 * Save the image to the Documents/OMRekap/folderName directory
+	 * @param context the application context
+	 * @param image the image to be saved
+	 * @param folderName the folder name where the image will be saved
+	 * @param fileName the file name of the image
+	 */
 	fun saveImage(
 		context: Context,
 		image: Bitmap,
@@ -79,6 +104,13 @@ object SaveHelper {
 		}
 	}
 
+	/**
+	 * Save the JSON to the Documents/OMRekap/folderName directory
+	 * @param context the application context
+	 * @param data the JSON data to be saved
+	 * @param folderName the folder name where the JSON will be saved
+	 * @param fileName the file name of the JSON
+	 */
 	private fun saveJSON(
 		context: Context,
 		data: Map<String, Int?>,
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/omr/ContourOMRHelper.kt b/app/src/main/java/com/k2_9/omrekap/utils/omr/ContourOMRHelper.kt
index fd04515..43fdba7 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/omr/ContourOMRHelper.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/omr/ContourOMRHelper.kt
@@ -21,11 +21,21 @@ import kotlin.math.floor
 import kotlin.math.max
 import kotlin.math.sin
 
+/**
+ * Helper for Optical Mark Recognition (OMR) using contours
+ * @param config configuration for the OMR helper
+ */
 class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(config) {
 	private var currentSectionGray: Mat? = null
 	private var currentSectionBinary: Mat? = null
 	public var appContext: Context? = null
 
+	/**
+	 * Create information object about the contour
+	 * @param center center of the contour
+	 * @param size size of the contour
+	 * @return ContourInfo object
+	 */
 	private fun createContourInfo(contour: Mat): ContourInfo {
 		val rect = Imgproc.boundingRect(contour)
 		val centerX = rect.x + rect.width / 2
@@ -33,6 +43,12 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		return ContourInfo(Pair(centerX, centerY), Pair(rect.width, rect.height))
 	}
 
+	/**
+	 * Filter contours based on the intensities and return the filtered contour infos
+	 * @param contourInfos list of contour infos
+	 * @param intensities list of intensities
+	 * @return filtered list of contour infos
+	 */
 	private fun getContourInfo(
 		filledContours: List<Mat>,
 		filledIntensities: List<Int>,
@@ -57,6 +73,11 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		return filterContourInfos(contourInfos, sortedIntensities.map { it.toDouble() })
 	}
 
+	/**
+	 * Predict the number based on the detected filled circle contours
+	 * @param contours list of filled circle contours
+	 * @return predicted number
+	 */
 	private fun predictForFilledCircle(contours: List<MatOfPoint>): Int {
 		// Predict the number based on the filled circle contours
 
@@ -97,6 +118,11 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		return contourInfosToNumbers(contourInfos)
 	}
 
+	/**
+	 * Get the darkest row in the column
+	 * @param colContours list of contours in the column
+	 * @return index of the darkest row
+	 */
 	private fun getDarkestRow(colContours: List<MatOfPoint>): Int? {
 		// Initialize variables to store the darkest row and its intensity
 		var darkestRow: Int? = null
@@ -137,6 +163,13 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		return darkestRow
 	}
 
+	/**
+	 * Create a perfect circle contour
+	 * @param x x-coordinate of the center
+	 * @param y y-coordinate of the center
+	 * @param radius radius of the circle
+	 * @return perfect circle contour
+	 */
 	private fun getPerfectCircle(x: Double, y: Double, radius: Double): MatOfPoint {
 		val numPoints = 100  // Adjust as needed
 		val theta = DoubleArray(numPoints) { it * 2 * Math.PI / numPoints }
@@ -151,6 +184,11 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		return circleContour
 	}
 
+	/**
+	 * Replace the contour with a perfect circle
+	 * @param contour contour to be replaced
+	 * @return perfect circle contour
+	 */
 	private fun replaceWithPerfectCircle(contour: MatOfPoint): MatOfPoint {
 		val rect = Imgproc.boundingRect(contour)
 		val centroidX = rect.x + rect.width.toDouble() / 2
@@ -160,6 +198,11 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		return getPerfectCircle(centroidX, centroidY, radius)
 	}
 
+	/**
+	 * Get the combined number from the darkest rows of each column, given 10 contours for each column
+	 * @param darkestRows list of 10 detected contours for each column
+	 * @return combined number
+	 */
 	private fun compareAll(contours: List<MatOfPoint>): Int {
 		// Sort contours by column and then by row
 		val contoursSorted = contours.sortedBy { Imgproc.boundingRect(it).x }
@@ -189,6 +232,12 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		}
 		return getCombinedNumbers(darkestRows.map { it ?: 0 })
 	}
+
+	/**
+	 * Complete missing contours by filling the missing circles
+	 * @param contours list of detected contours
+	 * @return list of completed contours
+	 */
 	private fun completeMissingContours(contours: List<MatOfPoint>): List<MatOfPoint> {
 		val sortedContours = contours.sortedBy { Imgproc.boundingRect(it).y }
 		val columnMap = Array(3) { mutableListOf<MatOfPoint>() }
@@ -283,6 +332,10 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		return result
 	}
 
+	/**
+	 * Detect the circles in the OMR section
+	 * @return list of detected contours
+	 */
 	private fun getAllContours(): List<MatOfPoint> {
 		// Find circle contours in cropped OMR section
 		val contours = mutableListOf<MatOfPoint>()
@@ -341,6 +394,11 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		return filteredContours
 	}
 
+	/**
+	 * Detect the number for the OMR section
+	 * @param contours list of detected contours
+	 * @return detected number
+	 */
 	override fun detect(section: OMRSection): Int {
 		val omrSectionImage = config.omrCropper.crop(section)
 
@@ -382,12 +440,20 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		}
 	}
 
-	// Get Section Position For Annotating Purpose
+	/**
+	 * Get the position of the OMR section
+	 * @param section OMR section
+	 * @return position of the OMR section
+	 */
 	fun getSectionPosition(section: OMRSection): Rect {
 		return config.omrCropper.sectionPosition(section)
 	}
 
-	// Annotating Image For Testing Purpose
+	/**
+	 * Annotate the image with the detected contour
+	 * @param contourNumber detected contour number
+	 * @return annotated image
+	 */
 	fun annotateImage(contourNumber: Int): Bitmap {
 		var annotatedImg = currentSectionGray!!.clone()
 		val contours = getAllContours()
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRConfigDetector.kt b/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRConfigDetector.kt
index 81b7e87..e31612e 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRConfigDetector.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRConfigDetector.kt
@@ -13,6 +13,9 @@ import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import org.opencv.core.Mat
 
+/**
+ * Detector for OMR configuration
+ */
 object OMRConfigDetector {
 	private lateinit var loadedConfig: OMRBaseConfiguration
 	private var job: Job? = null
@@ -20,6 +23,8 @@ object OMRConfigDetector {
 	/**
 	 * Initialize and load the detection configuration data.
 	 * Make sure to run this before detecting configurations
+	 * @param context application context
+	 * @throws Exception if failed to load the configuration
 	 */
 	fun loadConfiguration(context: Context) {
 		if (!this::loadedConfig.isInitialized) {
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRHelper.kt b/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRHelper.kt
index efdbb5e..adbf074 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRHelper.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRHelper.kt
@@ -5,12 +5,29 @@ import com.k2_9.omrekap.data.configs.omr.OMRSection
 import kotlin.math.abs
 import kotlin.math.floor
 
+/**
+ * Helper for Optical Mark Recognition (OMR)
+ * @param config configuration for the OMR helper
+ */
 abstract class OMRHelper(private val config: OMRHelperConfig) {
+	/**
+	 * Information about the contour
+	 * @param center center of the contour
+	 * @param size size of the contour
+	 */
 	class ContourInfo(val center: Pair<Int, Int>, val size: Pair<Int, Int>) {
+		/** Check if the contour is overlapping with another contour
+		 * @param other other contour to check
+		 * @return true if the contour is overlapping with the other contour, false otherwise
+		 */
 		fun isOverlapping(other: ContourInfo): Boolean {
 			return isColumnOverlapping(other) && isRowOverlapping(other)
 		}
 
+		/** Check if the contour is overlapping with another contour horizontally
+		 * @param other other contour to check
+		 * @return true if the contour is overlapping with the other contour horizontally, false otherwise
+		 */
 		fun isColumnOverlapping(other: ContourInfo): Boolean {
 			val x1 = center.first
 			val x2 = other.center.first
@@ -20,6 +37,10 @@ abstract class OMRHelper(private val config: OMRHelperConfig) {
 			return abs(x1 - x2) * 2 < w1 + w2
 		}
 
+		/** Check if the contour is overlapping with another contour vertically
+		 * @param other other contour to check
+		 * @return true if the contour is overlapping with the other contour vertically, false otherwise
+		 */
 		fun isRowOverlapping(other: ContourInfo): Boolean {
 			val y1 = center.second
 			val y2 = other.center.second
@@ -30,13 +51,27 @@ abstract class OMRHelper(private val config: OMRHelperConfig) {
 		}
 	}
 
+	/**
+	 * Error when detecting the filled circles
+	 * @param message error message
+	 */
 	class DetectionError(message: String) : Exception(message)
 
+	/**
+	 * Combine the detected numbers into a single integer
+	 * @param numbers list of detected numbers
+	 * @return combined numbers
+	 */
 	protected fun getCombinedNumbers(numbers: List<Int>): Int {
 		// Combine the detected numbers into a single integer
 		return numbers.joinToString("").toInt()
 	}
 
+	/**
+	 * Convert contour infos to numbers
+	 * @param contourInfos list of contour infos
+	 * @return detected numbers
+	 */
 	protected fun contourInfosToNumbers(contourInfos: List<ContourInfo?>): Int {
 		// Return the detected numbers based on the vertical position of the filled circles for each column
 		if (contourInfos.size != 3) {
@@ -62,6 +97,14 @@ abstract class OMRHelper(private val config: OMRHelperConfig) {
 		return getCombinedNumbers(result)
 	}
 
+	/**
+	 * Filter contour infos:
+	 * remove overlapping contour infos and choose the one with the highest intensity
+	 * automatically assign null to the column with no filled circle
+	 * @param contourInfos list of contour infos
+	 * @param filledIntensities list of filled intensities
+	 * @return filtered contour infos
+	 */
 	protected fun filterContourInfos(
 		contourInfos: List<ContourInfo>,
 		filledIntensities: List<Double>,
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/omr/TemplateMatchingOMRHelper.kt b/app/src/main/java/com/k2_9/omrekap/utils/omr/TemplateMatchingOMRHelper.kt
index d6a4814..1f5e795 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/omr/TemplateMatchingOMRHelper.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/omr/TemplateMatchingOMRHelper.kt
@@ -10,11 +10,18 @@ import org.opencv.core.Point
 import org.opencv.core.Rect
 import org.opencv.imgproc.Imgproc
 
+/**
+ * Helper for Optical Mark Recognition (OMR) using template matching
+ * @param config configuration for the OMR helper
+ */
 class TemplateMatchingOMRHelper(private val config: TemplateMatchingOMRHelperConfig) :
 	OMRHelper(config) {
 	private var currentSectionGray: Mat? = null
 	private var currentSectionBinary: Mat? = null
 
+	/** Get the rectangles of the matched template in the current section
+	 * @return list of pairs of rectangles and their similarity scores
+	 */
 	private fun getMatchRectangles(): List<Pair<Rect, Double>> {
 		// Load the template image
 		val template = config.template
@@ -67,6 +74,10 @@ class TemplateMatchingOMRHelper(private val config: TemplateMatchingOMRHelperCon
 		return matchedRectangles
 	}
 
+	/** Get the contour information from the matched rectangles
+	 * @param matchedRectangles list of pairs of rectangles and their similarity scores
+	 * @return pair of list of contour information and list of similarity scores
+	 */
 	private fun getContourInfos(matchedRectangles: List<Pair<Rect, Double>>): Pair<List<ContourInfo>, List<Double>> {
 		// Initialize a set to keep track of added rectangles
 		val addedRectangles = mutableSetOf<Rect>()
@@ -115,6 +126,10 @@ class TemplateMatchingOMRHelper(private val config: TemplateMatchingOMRHelperCon
 		return Pair(sortedContours, sortedSimilarities)
 	}
 
+	/** Annotation for the image with the detected filled circles
+	 * @param contourNumber detected number for the filled circles
+	 * @return annotated image as Bitmap
+	 */
 	fun annotateImage(contourNumber: Int): Bitmap {
 		val annotatedImg = currentSectionGray!!.clone()
 		val matchedRectangles = getMatchRectangles()
@@ -136,6 +151,10 @@ class TemplateMatchingOMRHelper(private val config: TemplateMatchingOMRHelperCon
 		return annotatedImageBitmap
 	}
 
+	/** Detect the filled circles in the section
+	 * @param section the OMR section to detect
+	 * @return detected number for the filled circles
+	 */
 	override fun detect(section: OMRSection): Int {
 		val omrSectionImage = config.omrCropper.crop(section)
 
-- 
GitLab