/// <summary> /// Nickの手法による二値化処理を行う。 /// </summary> /// <param name="imgSrc">入力画像</param> /// <param name="imgDst">出力画像</param> /// <param name="kernelSize">局所領域のサイズ</param> /// <param name="k">係数</param> #else /// <summary> /// Binarizes by Nick's method /// </summary> /// <param name="src">Input image</param> /// <param name="dst">Output image</param> /// <param name="kernelSize">Window size</param> /// <param name="k">Adequate coefficient</param> #endif public static void Nick(Mat src, Mat dst, int kernelSize, double k) { if (src == null) { throw new ArgumentNullException("src"); } if (dst == null) { throw new ArgumentNullException("dst"); } // グレースケールのみ if (src.Type() != MatType.CV_8UC1) { throw new ArgumentException("src must be gray scale image"); } if (dst.Type() != MatType.CV_8UC1) { throw new ArgumentException("dst must be gray scale image"); } // サイズのチェック if (kernelSize < 3) { throw new ArgumentOutOfRangeException("kernelSize", "size must be 3 and above"); } if (kernelSize % 2 == 0) { throw new ArgumentOutOfRangeException("kernelSize", "size must be odd number"); } int borderSize = kernelSize / 2; int width = src.Width; int height = src.Height; dst.Create(src.Size(), src.Type()); using (var tempMat = new Mat(height + (borderSize * 2), width + (borderSize * 2), src.Type())) using (var sumMat = new Mat(tempMat.Height + 1, tempMat.Width + 1, MatType.CV_64FC1, 1)) using (var sqSumMat = new Mat(tempMat.Height + 1, tempMat.Width + 1, MatType.CV_64FC1, 1)) { Cv2.CopyMakeBorder(src, tempMat, borderSize, borderSize, borderSize, borderSize, BorderTypes.Replicate, Scalar.All(0)); Cv2.Integral(tempMat, sumMat, sqSumMat); using (var tSrcMat = new MatOfByte(src)) using (var tDstMat = new MatOfByte(dst)) using (var tSumMat = new MatOfDouble(sumMat)) using (var tSqSumMat = new MatOfDouble(sqSumMat)) { var tSrc = tSrcMat.GetIndexer(); var tDst = tDstMat.GetIndexer(); var tSum = tSumMat.GetIndexer(); var tSqSum = tSqSumMat.GetIndexer(); int ylim = height + borderSize; int xlim = width + borderSize; int kernelPixels = kernelSize * kernelSize; for (int y = borderSize; y < ylim; y++) { for (int x = borderSize; x < xlim; x++) { int x1 = x - borderSize; int y1 = y - borderSize; int x2 = x + borderSize + 1; int y2 = y + borderSize + 1; double sum = tSum[y2, x2] - tSum[y2, x1] - tSum[y1, x2] + tSum[y1, x1]; double sqsum = tSqSum[y2, x2] - tSqSum[y2, x1] - tSqSum[y1, x2] + tSqSum[y1, x1]; double mean = sum / kernelPixels; double term = (sqsum - mean * mean) / kernelPixels; if (term < 0.0) { term = 0.0; } term = Math.Sqrt(term); double threshold = mean + k * term; if (tSrc[y - borderSize, x - borderSize] < threshold) { tDst[y - borderSize, x - borderSize] = 0; } else { tDst[y - borderSize, x - borderSize] = 255; } } } } } }
/// <summary> /// Niblackの手法による二値化処理を行う(高速だが、メモリを多く消費するバージョン)。 /// </summary> /// <param name="imgSrc">入力画像</param> /// <param name="imgDst">出力画像</param> /// <param name="kernelSize">局所領域のサイズ</param> /// <param name="k">係数</param> #else /// <summary> /// Binarizes by Niblack's method (This is faster but memory-hogging) /// </summary> /// <param name="src">Input image</param> /// <param name="dst">Output image</param> /// <param name="kernelSize">Window size</param> /// <param name="k">Adequate coefficient</param> #endif public static void NiblackFast(Mat src, Mat dst, int kernelSize, double k) { if (src == null) throw new ArgumentNullException("src"); if (dst == null) throw new ArgumentNullException("dst"); // グレースケールのみ if (src.Type() != MatType.CV_8UC1) throw new ArgumentException("src must be gray scale image"); if (dst.Type() != MatType.CV_8UC1) throw new ArgumentException("dst must be gray scale image"); // サイズのチェック if (kernelSize < 3) throw new ArgumentOutOfRangeException("kernelSize", "size must be 3 and above"); if (kernelSize % 2 == 0) throw new ArgumentOutOfRangeException("kernelSize", "size must be odd number"); int borderSize = kernelSize / 2; int width = src.Width; int height = src.Height; dst.Create(src.Size(), src.Type()); using (var tempMat = new Mat(height + (borderSize * 2), width + (borderSize * 2), src.Type())) using (var sumMat = new Mat(tempMat.Height + 1, tempMat.Width + 1, MatType.CV_64FC1, 1)) using (var sqSumMat = new Mat(tempMat.Height + 1, tempMat.Width + 1, MatType.CV_64FC1, 1)) { Cv2.CopyMakeBorder(src, tempMat, borderSize, borderSize, borderSize, borderSize, BorderTypes.Replicate, Scalar.All(0)); Cv2.Integral(tempMat, sumMat, sqSumMat); using (var tSrcMat = new MatOfByte(src)) using (var tDstMat = new MatOfByte(dst)) using (var tSumMat = new MatOfDouble(sumMat)) using (var tSqSumMat = new MatOfDouble(sqSumMat)) { var tSrc = tSrcMat.GetIndexer(); var tDst = tDstMat.GetIndexer(); var tSum = tSumMat.GetIndexer(); var tSqSum = tSqSumMat.GetIndexer(); int ylim = height + borderSize; int xlim = width + borderSize; int kernelPixels = kernelSize * kernelSize; for (int y = borderSize; y < ylim; y++) { for (int x = borderSize; x < xlim; x++) { int x1 = x - borderSize; int y1 = y - borderSize; int x2 = x + borderSize + 1; int y2 = y + borderSize + 1; double sum = tSum[y2, x2] - tSum[y2, x1] - tSum[y1, x2] + tSum[y1, x1]; double sqsum = tSqSum[y2, x2] - tSqSum[y2, x1] - tSqSum[y1, x2] + tSqSum[y1, x1]; double mean = sum / kernelPixels; double var = (sqsum / kernelPixels) - (mean * mean); if (var < 0.0) var = 0.0; double stddev = Math.Sqrt(var); double threshold = mean + k * stddev; if (tSrc[y - borderSize, x - borderSize] < threshold) tDst[y - borderSize, x - borderSize] = 0; else tDst[y - borderSize, x - borderSize] = 255; } } } } }