diff --git a/app/src/androidTest/java/com/k2_9/omrekap/aprilTag/AprilTagConfigDetectionTest.kt b/app/src/androidTest/java/com/k2_9/omrekap/aprilTag/AprilTagConfigDetectionTest.kt
index ff11227c2dd6ff000dbb2d534eb2920d2ce008dc..6d80b4578042cb41dfb8f26d04836ee111d1f8f9 100644
--- a/app/src/androidTest/java/com/k2_9/omrekap/aprilTag/AprilTagConfigDetectionTest.kt
+++ b/app/src/androidTest/java/com/k2_9/omrekap/aprilTag/AprilTagConfigDetectionTest.kt
@@ -6,7 +6,7 @@ import androidx.test.platform.app.InstrumentationRegistry
 import com.google.gson.Gson
 import com.k2_9.omrekap.R
 import com.k2_9.omrekap.data.repository.OMRConfigRepository
-import com.k2_9.omrekap.utils.omr.OMRConfigurationDetector
+import com.k2_9.omrekap.utils.omr.OMRConfigDetector
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -31,10 +31,10 @@ class AprilTagConfigDetectionTest {
 		Imgproc.cvtColor(imageMat, grayImageMat, Imgproc.COLOR_BGR2GRAY)
 
 		CoroutineScope(Dispatchers.Default).launch {
-			OMRConfigurationDetector.loadConfiguration(
+			OMRConfigDetector.loadConfiguration(
 				appContext
 			)
-			val result = OMRConfigurationDetector.detectConfiguration(grayImageMat)
+			val result = OMRConfigDetector.detectConfiguration(grayImageMat)
 			val gson = Gson()
 			Log.d("ConfigDetectionTestx", gson.toJson(result))
 			val compare = OMRConfigRepository.loadConfigurations(appContext)
diff --git a/app/src/androidTest/java/com/k2_9/omrekap/preprocess/CropHelperTest.kt b/app/src/androidTest/java/com/k2_9/omrekap/preprocess/CropHelperTest.kt
index 6d4164215a341bcdae247a6e4f569b0958c7a6b2..a5522c034fa3da52221a9fe5f25562bd6138de82 100644
--- a/app/src/androidTest/java/com/k2_9/omrekap/preprocess/CropHelperTest.kt
+++ b/app/src/androidTest/java/com/k2_9/omrekap/preprocess/CropHelperTest.kt
@@ -35,7 +35,11 @@ class CropHelperTest {
 		image = Utils.loadResource(appContext, R.raw.example, CvType.CV_8UC1)
 		patternImage = Utils.loadResource(appContext, R.raw.corner_pattern, CvType.CV_8UC4)
 
-		patternBitmap = Bitmap.createBitmap(patternImage.width(), patternImage.height(), Bitmap.Config.ARGB_8888)
+		patternBitmap = Bitmap.createBitmap(
+			patternImage.width(),
+			patternImage.height(),
+			Bitmap.Config.ARGB_8888
+		)
 		imageBitmap = Bitmap.createBitmap(image.width(), image.height(), Bitmap.Config.ARGB_8888)
 		Utils.matToBitmap(image, imageBitmap)
 		Utils.matToBitmap(patternImage, patternBitmap)
@@ -56,7 +60,12 @@ class CropHelperTest {
 		imageSaveData = PreprocessHelper.preprocessImage(imageSaveData)
 
 		SaveHelper.saveImage(appContext, imageSaveData.rawImage, "test", "test_preprocess_raw")
-		SaveHelper.saveImage(appContext, imageSaveData.annotatedImage, "test", "test_preprocess_annotated")
+		SaveHelper.saveImage(
+			appContext,
+			imageSaveData.annotatedImage,
+			"test",
+			"test_preprocess_annotated"
+		)
 	}
 
 	@After
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 69bee099f9d12b00e4ca7d566fd3a6b8eceb99b6..fcbfe5a078cc889c20de1554bdbc76bc2c4adb58 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
@@ -33,7 +33,7 @@ object OMRConfigRepository {
 		return try {
 			val buffer = ByteArray(inputStream.available())
 			inputStream.read(buffer)
-			Log.d("OMRConfigLoader", String(buffer))
+//			Log.d("OMRConfigLoader", String(buffer))
 
 			String(buffer)
 		} catch (e: IOException) {
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 c2b93ce92a9476955782096493ae76ba338f6e04..83a090511274daa7e968e7207ade9a6474ee076d 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
@@ -1,12 +1,16 @@
 package com.k2_9.omrekap.data.view_models
 
-import android.graphics.Bitmap
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import com.k2_9.omrekap.data.models.ImageSaveData
+import com.k2_9.omrekap.utils.AprilTagHelper
+import com.k2_9.omrekap.utils.omr.OMRConfigDetector
 import kotlinx.coroutines.launch
+import org.opencv.android.Utils
+import org.opencv.core.Mat
+import org.opencv.imgproc.Imgproc
 
 class ImageDataViewModel : ViewModel() {
 	private val _data = MutableLiveData<ImageSaveData>()
@@ -14,7 +18,23 @@ class ImageDataViewModel : ViewModel() {
 
 	fun processImage(data: ImageSaveData) {
 		viewModelScope.launch {
+			val rawImage = data.rawImage
+			val imageMat = Mat()
+//			val annotatedImageMat = Mat()
+			Utils.bitmapToMat(rawImage, imageMat)
+
+			// convert image to gray
+			val grayImageMat = Mat()
+			Imgproc.cvtColor(imageMat, grayImageMat, Imgproc.COLOR_BGR2GRAY)
+
+			// load configuration
+			val (loadedConfig, id, corners) = OMRConfigDetector.detectConfiguration(grayImageMat)!!
+
+			// annotate the detected AprilTag
+			val annotatedImage = AprilTagHelper.annotateImage(rawImage)
+
 			// TODO: Process the raw image using OMRHelper
+			data.annotatedImage = annotatedImage
 			_data.value = data
 		}
 	}
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 bb1d90bd650facb8e1ac5d486c3f13a100420a2f..4f1e9f8fc661413d40f00f86ebd4c9e2fa774bbd 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
@@ -61,6 +61,13 @@ object AprilTagHelper {
 		for (i in 0..<nId) {
 			val id = idMat[i, 0][0].toInt().toString()
 			logDebug("detected tag with id: $id")
+			val cornerPoints = corners[i]
+			logDebug(
+				"with corners at: (${cornerPoints[0, 0][0]},${cornerPoints[0, 0][1]}), " +
+					"(${cornerPoints[0, 1][0]},${cornerPoints[0, 1][1]}) " +
+					"(${cornerPoints[0, 2][0]},${cornerPoints[0, 2][1]}) " +
+					"(${cornerPoints[0, 3][0]},${cornerPoints[0, 3][1]})"
+			)
 			idList.add(id)
 		}
 
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 8a2cb6e34a0a7bccd9b732d492fd2a81495c6d0c..bed44fbe62849938b690da790792980cc049e30a 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
@@ -28,19 +28,46 @@ object ImageAnnotationHelper {
 	): Mat {
 		val imgWithAnnotations = img.clone()
 		if (id.isNotEmpty()) {
+			// points -> list<Point*s*>, inside list of points are corners of the detector
 			val points =
 				cornerPoints.map { mat ->
-					val x = mat.get(0, 0)[0]
-					val y = mat.get(1, 0)[0]
-					Point(x, y)
+					val points = ArrayList<Point>()
+					for (i in 0..<4) {
+						val x = mat.get(0, i)[0]
+						val y = mat.get(0, i)[1]
+						points.add(Point(x, y))
+					}
+					points
 				}
 
 			// Draw ID and bounding box
-			Imgproc.putText(imgWithAnnotations, id, points[0], Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0.0, 255.0, 0.0), 5)
-			Imgproc.polylines(imgWithAnnotations, listOf(MatOfPoint(*points.toTypedArray())), true, Scalar(0.0, 255.0, 0.0), 5)
+			Imgproc.putText(
+				imgWithAnnotations,
+				id,
+				points[0][0],
+				Imgproc.FONT_HERSHEY_SIMPLEX,
+				1.0,
+				Scalar(0.0, 255.0, 0.0),
+				5
+			)
+			Imgproc.polylines(
+				imgWithAnnotations,
+				listOf(MatOfPoint(*points[0].toTypedArray())),
+				true,
+				Scalar(0.0, 255.0, 0.0),
+				5
+			)
 		} else {
 			val topLeft = Point(cornerPoints[0].get(0, 0)[0], cornerPoints[0].get(1, 0)[0])
-			Imgproc.putText(imgWithAnnotations, "April Tag Not Detected", topLeft, Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0.0, 255.0, 0.0), 5)
+			Imgproc.putText(
+				imgWithAnnotations,
+				"April Tag Not Detected",
+				topLeft,
+				Imgproc.FONT_HERSHEY_SIMPLEX,
+				1.0,
+				Scalar(0.0, 255.0, 0.0),
+				5
+			)
 		}
 		return imgWithAnnotations
 	}
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 2ec887351e78e29105acad6d4d9c0bf7dec8278b..d155d1785fb7a25355f42e90f6d87e1f528ce140 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
@@ -118,7 +118,8 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		// Loop through each column
 		for (col in 0 until 3) {
 			// Get contours for the current column and sort by rows
-			val colContours = contoursSorted.subList(col * 10, (col + 1) * 10).sortedBy { Imgproc.boundingRect(it).y }
+			val colContours = contoursSorted.subList(col * 10, (col + 1) * 10)
+				.sortedBy { Imgproc.boundingRect(it).y }
 
 			val darkestRow = getDarkestRow(colContours)
 
@@ -138,7 +139,13 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		// Find circle contours in cropped OMR section
 		val contours = mutableListOf<MatOfPoint>()
 		val hierarchy = Mat()
-		Imgproc.findContours(currentSectionBinary!!, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE)
+		Imgproc.findContours(
+			currentSectionBinary!!,
+			contours,
+			hierarchy,
+			Imgproc.RETR_EXTERNAL,
+			Imgproc.CHAIN_APPROX_SIMPLE
+		)
 
 		// Initialize a list to store filtered contours
 		val filteredContours = mutableListOf<MatOfPoint>()
@@ -155,7 +162,10 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 			if (rect.width in minLength..maxLength && rect.height in minLength..maxLength && ar >= minAR && ar <= maxAR) {
 				filteredContours.add(contour)
 			} else {
-				Log.d("ContourOMRHelper", "Contour with aspect ratio $ar and size ${rect.width} x ${rect.height} filtered out")
+				Log.d(
+					"ContourOMRHelper",
+					"Contour with aspect ratio $ar and size ${rect.width} x ${rect.height} filtered out"
+				)
 			}
 		}
 
@@ -165,9 +175,14 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 	fun annotateImage(contourNumber: Int): Bitmap {
 		var annotatedImg = currentSectionGray!!.clone()
 		val contours = getAllContours()
-		annotatedImg = ImageAnnotationHelper.annotateContourOMR(annotatedImg, contours, contourNumber)
-
-		val annotatedImageBitmap = Bitmap.createBitmap(annotatedImg.width(), annotatedImg.height(), Bitmap.Config.ARGB_8888)
+		annotatedImg =
+			ImageAnnotationHelper.annotateContourOMR(annotatedImg, contours, contourNumber)
+
+		val annotatedImageBitmap = Bitmap.createBitmap(
+			annotatedImg.width(),
+			annotatedImg.height(),
+			Bitmap.Config.ARGB_8888
+		)
 		Utils.matToBitmap(annotatedImg, annotatedImageBitmap)
 		return annotatedImageBitmap
 	}
@@ -181,7 +196,13 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 
 		// Apply binary thresholding
 		val binary = Mat()
-		Imgproc.threshold(gray, binary, 0.0, 255.0, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_TRIANGLE)
+		Imgproc.threshold(
+			gray,
+			binary,
+			0.0,
+			255.0,
+			Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_TRIANGLE
+		)
 
 		// Update states
 		currentSectionGray = gray
@@ -190,7 +211,10 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 		val contours = getAllContours()
 
 		return if (contours.size != 30) {
-			Log.d("ContourOMRHelper", "Some circles are not detected, considering only filled circles")
+			Log.d(
+				"ContourOMRHelper",
+				"Some circles are not detected, considering only filled circles"
+			)
 			predictForFilledCircle(contours)
 		} else {
 			Log.d("ContourOMRHelper", "All 30 circles are detected")
diff --git a/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRConfigurationDetector.kt b/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRConfigDetector.kt
similarity index 62%
rename from app/src/main/java/com/k2_9/omrekap/utils/omr/OMRConfigurationDetector.kt
rename to app/src/main/java/com/k2_9/omrekap/utils/omr/OMRConfigDetector.kt
index 42b66317fd7972b1af6ba7ee74435284f4c8817e..8f542416cee9391810d19ce1dee6b497e509bae2 100644
--- a/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRConfigurationDetector.kt
+++ b/app/src/main/java/com/k2_9/omrekap/utils/omr/OMRConfigDetector.kt
@@ -6,32 +6,42 @@ import com.k2_9.omrekap.data.models.OMRBaseConfiguration
 import com.k2_9.omrekap.data.models.OMRConfigurationParameter
 import com.k2_9.omrekap.data.repository.OMRConfigRepository
 import com.k2_9.omrekap.utils.AprilTagHelper
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import org.opencv.core.Mat
 
-object OMRConfigurationDetector {
+object OMRConfigDetector {
 	private lateinit var loadedConfig: OMRBaseConfiguration
+	private var job: Job? = null
 
 	/**
 	 * Initialize and load the detection configuration data.
 	 * Make sure to run this before detecting configurations
 	 */
-	suspend fun loadConfiguration(context: Context) {
-		loadedConfig = OMRConfigRepository.loadConfigurations(context)
-			?: throw Exception("Failed to load OMR Configuration!")
+	fun loadConfiguration(context: Context) {
+		if (!this::loadedConfig.isInitialized) {
+			job = CoroutineScope(Dispatchers.IO).launch {
+				loadedConfig = OMRConfigRepository.loadConfigurations(context)
+					?: throw Exception("Failed to load OMR Configuration!")
+			}
+		}
 	}
 
 	/**
 	 * Detects the OMR configuration of an image to be processed.
 	 * @param imageMat pre-processed image in gray or non color form
-	 * @return Pair of OMR configuration and the image's tag corners that was used for configuration detector
+	 * @return Triple of OMR configuration, the ID of the detected AprilTag,
+	 * and the image's tag corners that was used for configuration detector
 	 */
 	suspend fun detectConfiguration(imageMat: Mat):
-		Pair<OMRConfigurationParameter, Mat>? {
+		Triple<OMRConfigurationParameter, String, Mat>? {
+		job?.join().also { job = null }
 		val configs = loadedConfig.omrConfigs
 
-		var result: Pair<OMRConfigurationParameter, Mat>? = null
+		var result: Triple<OMRConfigurationParameter, String, Mat>? = null
 		withContext(Dispatchers.Default) {
 			// get detected AprilTags
 			val (ids, cornersList) = AprilTagHelper.getAprilTagId(imageMat)
@@ -40,7 +50,7 @@ object OMRConfigurationDetector {
 				val id = ids[i]
 				if (id in configs) {
 					if (result == null) {
-						result = configs[id]!! to cornersList[i]
+						result = Triple(configs[id]!!, id, cornersList[i])
 					} else {
 						Log.e(
 							"OMRConfigurationDetector",
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 5cb0b42f94be857b7a94972cb6c17112f0362770..d36e7ca5064e84aea973fc44323fcbe461350799 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
@@ -9,9 +9,9 @@ import org.opencv.core.Mat
 import org.opencv.core.Point
 import org.opencv.core.Rect
 import org.opencv.imgproc.Imgproc
-import kotlin.collections.ArrayList
 
-class TemplateMatchingOMRHelper(private val config: TemplateMatchingOMRHelperConfig) : OMRHelper(config) {
+class TemplateMatchingOMRHelper(private val config: TemplateMatchingOMRHelperConfig) :
+	OMRHelper(config) {
 	private var currentSectionGray: Mat? = null
 	private var currentSectionBinary: Mat? = null
 
@@ -23,11 +23,22 @@ class TemplateMatchingOMRHelper(private val config: TemplateMatchingOMRHelperCon
 
 		// Apply binary thresholding to the template image
 		val templateBinary = Mat()
-		Imgproc.threshold(template, templateBinary, 0.0, 255.0, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_TRIANGLE)
+		Imgproc.threshold(
+			template,
+			templateBinary,
+			0.0,
+			255.0,
+			Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_TRIANGLE
+		)
 
 		// Perform template matching
 		val result = Mat()
-		Imgproc.matchTemplate(currentSectionBinary, templateBinary, result, Imgproc.TM_CCOEFF_NORMED)
+		Imgproc.matchTemplate(
+			currentSectionBinary,
+			templateBinary,
+			result,
+			Imgproc.TM_CCOEFF_NORMED
+		)
 
 		// Set a threshold for template matching result
 		val threshold = config.similarityThreshold
@@ -102,7 +113,11 @@ class TemplateMatchingOMRHelper(private val config: TemplateMatchingOMRHelperCon
 	fun annotateImage(contourNumber: Int): Bitmap {
 		val annotatedImg = currentSectionGray!!.clone()
 		val matchedRectangles = getMatchRectangles()
-		val res = ImageAnnotationHelper.annotateTemplateMatchingOMR(annotatedImg, matchedRectangles, contourNumber)
+		val res = ImageAnnotationHelper.annotateTemplateMatchingOMR(
+			annotatedImg,
+			matchedRectangles,
+			contourNumber
+		)
 
 		// Convert the annotated Mat to Bitmap
 		val annotatedImageBitmap =
@@ -124,7 +139,13 @@ class TemplateMatchingOMRHelper(private val config: TemplateMatchingOMRHelperCon
 
 		// Apply binary thresholding
 		val binary = Mat()
-		Imgproc.threshold(gray, binary, 0.0, 255.0, Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_TRIANGLE)
+		Imgproc.threshold(
+			gray,
+			binary,
+			0.0,
+			255.0,
+			Imgproc.THRESH_BINARY_INV + Imgproc.THRESH_TRIANGLE
+		)
 
 		// Update states
 		currentSectionGray = gray
diff --git a/app/src/main/java/com/k2_9/omrekap/views/activities/ResultActivity.kt b/app/src/main/java/com/k2_9/omrekap/views/activities/ResultActivity.kt
index f6d5f7352a55d28f8202d56a2951c19b6d6ae054..fcdca172937c92f358445c6176915fe38f66a75b 100644
--- a/app/src/main/java/com/k2_9/omrekap/views/activities/ResultActivity.kt
+++ b/app/src/main/java/com/k2_9/omrekap/views/activities/ResultActivity.kt
@@ -18,6 +18,7 @@ import com.k2_9.omrekap.data.view_models.ImageDataViewModel
 import com.k2_9.omrekap.utils.ImageSaveDataHolder
 import com.k2_9.omrekap.utils.PermissionHelper
 import com.k2_9.omrekap.utils.SaveHelper
+import com.k2_9.omrekap.utils.omr.OMRConfigDetector
 import com.k2_9.omrekap.views.fragments.ResultPageFragment
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
@@ -129,10 +130,15 @@ abstract class ResultActivity : MainActivity() {
 	override fun onCreate(savedInstanceState: Bundle?) {
 		super.onCreate(savedInstanceState)
 
+		OMRConfigDetector.loadConfiguration(this)
 		OpenCVLoader.initLocal()
 
 		if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
-			PermissionHelper.requirePermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, false) {}
+			PermissionHelper.requirePermission(
+				this,
+				Manifest.permission.WRITE_EXTERNAL_STORAGE,
+				false
+			) {}
 		}
 
 		startSaveJob = savedInstanceState?.getBoolean("startSaveJob") ?: false