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 1ba7b5820f40533fa82b1f5b9de72f2868afb59d..bfa8ed238f76c2884a1175ba201b4a9c17ebfe83 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
@@ -94,11 +94,12 @@ class ImageDataViewModel : ViewModel() {
 				for ((section, value) in it) {
 					stringKeyResult[pageContent[section]!!] = value
 
-					annotatedImage = ImageAnnotationHelper.annotateOMR(
-						annotatedImage,
-						contourOMRHelper.getSectionPosition(section),
-						value
-					)
+					annotatedImage =
+						ImageAnnotationHelper.annotateOMR(
+							annotatedImage,
+							contourOMRHelper.getSectionPosition(section),
+							value,
+						)
 					Log.d("Result", "${pageContent[section]}: $value")
 				}
 			}
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 03324a2addefc824b2a78aabf05cd3c0b38adfca..57b8e465b689af083551e8e6d1c084e63c54a8a9 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
@@ -4,15 +4,18 @@ import android.graphics.Bitmap
 import android.util.Log
 import com.k2_9.omrekap.data.models.CornerPoints
 import org.opencv.android.Utils
+import org.opencv.core.Core
 import org.opencv.core.CvType
 import org.opencv.core.Mat
 import org.opencv.core.MatOfPoint2f
 import org.opencv.core.Point
+import org.opencv.core.Size
 import org.opencv.imgproc.Imgproc
 import org.opencv.imgproc.Imgproc.COLOR_BGR2GRAY
 import org.opencv.imgproc.Imgproc.cvtColor
 import org.opencv.imgproc.Imgproc.getPerspectiveTransform
 import org.opencv.imgproc.Imgproc.warpPerspective
+import kotlin.math.min
 import kotlin.math.pow
 import kotlin.math.sqrt
 
@@ -54,9 +57,12 @@ object CropHelper {
 			throw Exception("Pattern not loaded!")
 		}
 
-		val imgGray = img.clone()
+		var imgGray = img.clone()
 		cvtColor(img, imgGray, COLOR_BGR2GRAY)
 
+		// do local normalization here
+		imgGray = localNormalize(imgGray)
+
 		val resultMatrix =
 			Mat(
 				img.height() - pattern.height() + 1,
@@ -81,10 +87,22 @@ object CropHelper {
 		)
 
 		val pointsList: MutableList<PointsAndWeight> = mutableListOf()
-
-		for (i in 0 until resultMatrix.height() step 4) {
-			for (j in 0 until resultMatrix.width() step 4) {
-				pointsList.add(PointsAndWeight(i, j, resultMatrix.get(i, j)[0]))
+		val diagonalLength = (resultMatrix.height().toDouble().pow(2) * resultMatrix.width().toDouble().pow(2)).pow(1 / 2)
+
+		for (i in 0 until resultMatrix.height() step 2) {
+			for (j in 0 until resultMatrix.width() step 2) {
+				pointsList.add(
+					PointsAndWeight(
+						i,
+						j,
+						resultMatrix.get(i, j)[0] *
+							getWeight(
+								(
+									min(i, resultMatrix.height() - i).toDouble().pow(2) * min(j, resultMatrix.width() - j).toDouble().pow(2)
+								).pow(1 / 2) / diagonalLength,
+							),
+					),
+				)
 			}
 		}
 
@@ -93,9 +111,22 @@ object CropHelper {
 		pointsList.forEach {
 			if (needChange == 0) return@forEach
 
-			val corner = nearWhichCorner(it.x, it.y, resultMatrix.height(), resultMatrix.width(), limFrac = 0.1F)
+			val corner = nearWhichCorner(it.x, it.y, resultMatrix.height(), resultMatrix.width(), limFrac = 0.6F)
 			if (corner == -1) return@forEach
 
+			if (it.weight > 0.45) {
+				// Corner not found, throw exception
+				val exceptionMessage =
+					"Not all corners found: {" +
+						(if (needed[0]) "Upper left," else "") +
+						(if (needed[1]) "Upper right," else "") +
+						(if (needed[2]) "Lower right," else "") +
+						(if (needed[3]) "Lower left," else "") +
+						"}"
+				// throw NotFoundException(exceptionMessage);
+				Log.e("Corner", exceptionMessage)
+			}
+
 			if (needed[corner]) {
 				needed[corner] = false
 				needChange--
@@ -238,4 +269,56 @@ object CropHelper {
 			else -> -1
 		}
 	}
+
+	/**
+	 * Apply local normalization to an image
+	 * source: https://stackoverflow.com/questions/43240604/python-local-normalization-in-opencv
+	 * @see - https://bigwww.epfl.ch/demo/ip/demos/local-normalization/
+	 *
+	 * @param img input matrix
+	 * @return local normalized img
+	 */
+	private fun localNormalize(img: Mat): Mat {
+		// convert img to CV_32F
+		val gray = Mat()
+		img.convertTo(gray, CvType.CV_32F, 1.0 / 255.0)
+
+		val blur = Mat()
+		Imgproc.GaussianBlur(gray, blur, Size(0.0, 0.0), 2.0, 2.0)
+
+		val num = Mat()
+		Core.subtract(gray, blur, num)
+
+		val numSquared = Mat()
+		Core.multiply(num, num, numSquared)
+		val blur2 = Mat()
+		Imgproc.GaussianBlur(numSquared, blur2, Size(0.0, 0.0), 20.0, 20.0)
+
+		val den = Mat()
+		Core.sqrt(blur2, den)
+
+		val div = Mat()
+		Core.divide(num, den, div)
+
+		Core.normalize(div, div, 0.0, 1.0, Core.NORM_MINMAX)
+
+		// Convert back to uint8
+		val result = Mat()
+		div.convertTo(result, CvType.CV_8U, 255.0)
+
+		return result
+	}
+
+	/**
+	 * Weight for a point
+	 * Far from corner means bigger weight
+	 *
+	 * Smaller weight are more likely to be chosen as corner
+	 *
+	 * @param normDistance distance from nearest corner divided by diagonal
+	 * @return weight
+	 */
+	private fun getWeight(normDistance: Double): Double {
+		return 1 + 1 * normDistance.pow(1.5)
+	}
 }
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 43fdba73e5de9b82a363e90451481e1bc0993afd..c60aaea0cfdacaf3a0f5131cce2b206254350625 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
@@ -170,15 +170,19 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 	 * @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
+	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 }
 		val circleX = DoubleArray(numPoints) { x + radius * cos(theta[it]) }
 		val circleY = DoubleArray(numPoints) { y + radius * sin(theta[it]) }
 
 		val circleContour = MatOfPoint()
 		for (i in 0 until numPoints) {
-			circleContour.push_back(MatOfPoint(Point(circleX[i], circleY[i])));
+			circleContour.push_back(MatOfPoint(Point(circleX[i], circleY[i])))
 		}
 
 		return circleContour
@@ -388,7 +392,6 @@ class ContourOMRHelper(private val config: ContourOMRHelperConfig) : OMRHelper(c
 
 			Utils.matToBitmap(display, bitmap)
 			SaveHelper.saveImage(appContext!!, bitmap, "test", "lol.png")
-
 		}
 
 		return filteredContours
diff --git a/app/src/main/java/com/k2_9/omrekap/views/activities/PreviewActivity.kt b/app/src/main/java/com/k2_9/omrekap/views/activities/PreviewActivity.kt
index 240353e7fb36bdba05d31a7b1bf4440eb645ca60..bd37f1d804eb529ed06dceffd24edd6f1dcd1d07 100644
--- a/app/src/main/java/com/k2_9/omrekap/views/activities/PreviewActivity.kt
+++ b/app/src/main/java/com/k2_9/omrekap/views/activities/PreviewActivity.kt
@@ -50,7 +50,7 @@ class PreviewActivity : AppCompatActivity() {
 		val bitmapOptions = BitmapFactory.Options()
 		bitmapOptions.inPreferredConfig = Bitmap.Config.ALPHA_8
 		bitmapOptions.inScaled = false
-		val cornerPatternBitmap: Bitmap = BitmapFactory.decodeResource(resources, R.raw.corner_pattern, bitmapOptions)
+		val cornerPatternBitmap: Bitmap = BitmapFactory.decodeResource(resources, R.raw.mark20, bitmapOptions)
 
 		CropHelper.loadPattern(cornerPatternBitmap)
 
diff --git a/app/src/main/res/raw/mark16.png b/app/src/main/res/raw/mark16.png
new file mode 100644
index 0000000000000000000000000000000000000000..e34e5106f50567a9baecca15a4ba92bd1c58670f
Binary files /dev/null and b/app/src/main/res/raw/mark16.png differ
diff --git a/app/src/main/res/raw/mark20.png b/app/src/main/res/raw/mark20.png
new file mode 100644
index 0000000000000000000000000000000000000000..7c65cbf6ea9c87669d1c11dec0d6d510b7ac3561
Binary files /dev/null and b/app/src/main/res/raw/mark20.png differ