Improve distinction between color and grayscale documents (#36)
This commit is contained in:
@@ -40,21 +40,63 @@ fun enhanceCapturedImage(img: Mat): Mat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isColoredDocument(img: Mat, threshold: Double = 4.0): Boolean {
|
fun isColoredDocument(
|
||||||
|
img: Mat,
|
||||||
|
chromaThreshold: Double = 20.0,
|
||||||
|
proportionThreshold: Double = 0.001
|
||||||
|
): Boolean {
|
||||||
val lab = Mat()
|
val lab = Mat()
|
||||||
Imgproc.cvtColor(img, lab, Imgproc.COLOR_BGR2Lab)
|
Imgproc.cvtColor(img, lab, Imgproc.COLOR_BGR2Lab)
|
||||||
|
|
||||||
val channels = ArrayList<Mat>()
|
val channels = ArrayList<Mat>()
|
||||||
Core.split(lab, channels)
|
Core.split(lab, channels)
|
||||||
|
val a = channels[1]
|
||||||
|
val b = channels[2]
|
||||||
|
|
||||||
val aStd = MatOfDouble()
|
val aFloat = Mat()
|
||||||
val bStd = MatOfDouble()
|
val bFloat = Mat()
|
||||||
Core.meanStdDev(channels[1], MatOfDouble(), aStd)
|
a.convertTo(aFloat, CvType.CV_32F)
|
||||||
Core.meanStdDev(channels[2], MatOfDouble(), bStd)
|
b.convertTo(bFloat, CvType.CV_32F)
|
||||||
|
|
||||||
val result = (aStd.toArray()[0] + bStd.toArray()[0]) / 2.0
|
val aShifted = Mat()
|
||||||
return result > threshold
|
val bShifted = Mat()
|
||||||
|
Core.subtract(aFloat, Scalar(128.0), aShifted)
|
||||||
|
Core.subtract(bFloat, Scalar(128.0), bShifted)
|
||||||
|
|
||||||
|
val aSq = Mat()
|
||||||
|
val bSq = Mat()
|
||||||
|
Core.multiply(aShifted, aShifted, aSq)
|
||||||
|
Core.multiply(bShifted, bShifted, bSq)
|
||||||
|
|
||||||
|
val sumSq = Mat()
|
||||||
|
Core.add(aSq, bSq, sumSq)
|
||||||
|
|
||||||
|
val chroma = Mat()
|
||||||
|
Core.sqrt(sumSq, chroma)
|
||||||
|
|
||||||
|
val mask = Mat()
|
||||||
|
Imgproc.threshold(chroma, mask, chromaThreshold, 1.0, Imgproc.THRESH_BINARY)
|
||||||
|
val coloredPixels = Core.countNonZero(mask)
|
||||||
|
|
||||||
|
val totalPixels = chroma.rows() * chroma.cols()
|
||||||
|
val proportion = coloredPixels.toDouble() / totalPixels.toDouble()
|
||||||
|
|
||||||
|
lab.release()
|
||||||
|
channels.forEach { it.release() }
|
||||||
|
aFloat.release()
|
||||||
|
bFloat.release()
|
||||||
|
aShifted.release()
|
||||||
|
bShifted.release()
|
||||||
|
aSq.release()
|
||||||
|
bSq.release()
|
||||||
|
sumSq.release()
|
||||||
|
chroma.release()
|
||||||
|
mask.release()
|
||||||
|
|
||||||
|
return proportion > proportionThreshold
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun multiScaleRetinex(img: Mat, kernelSizes: List<Double> = listOf(30.0, 500.0)): Mat {
|
private fun multiScaleRetinex(img: Mat, kernelSizes: List<Double> = listOf(30.0, 500.0)): Mat {
|
||||||
// Convert to grayscale (1 channel)
|
// Convert to grayscale (1 channel)
|
||||||
val gray = Mat()
|
val gray = Mat()
|
||||||
|
|||||||
Reference in New Issue
Block a user