public KrecImage ApplyTransformMatrix(KrecImage image, List <Point> corners) { var matrix = GetMatrix(corners); var width = image.Width; var height = image.Height; var bytesPerPixel = image.Format.BytesPerPixel(); var result = new KrecImage(image, new byte[image.ImageData.Length]); for (var lineIdx = 0; lineIdx < height; lineIdx++) { for (int counter = 0, sourceIdx = lineIdx * image.BytesPerLine; counter < width; counter++, sourceIdx += bytesPerPixel) { var point = new [, ] { { (double)counter / width, (double)lineIdx / height, 1 } }; var color = GetСolor(MultiplyMatrix(point, matrix), image, bytesPerPixel); for (var i = 0; i < bytesPerPixel; i++) { result.ImageData[sourceIdx + i] = color[i]; } } } return(result); }
/// <summary> /// Calculate deskew angle of bitmap /// </summary> /// <param name="bmp">Skewed bitmap</param> /// <returns>Skew angle</returns> public static double CalculateAngle(Bitmap bmp) { var sourceImage = KrecImage.FromBitmap(bmp); var grayscaledImage = sourceImage.ToGrayscaled(); return(CalculateAngle(grayscaledImage)); }
/// <summary> /// Compress image to specified sizes /// </summary> /// <param name="grayscaleImage">Original image in grayscale</param> /// <param name="newWidth">Result width</param> /// <param name="newHeight">Result height</param> /// <returns>Compressed image</returns> public static KrecImage CompressImageToNewSizes(KrecImage grayscaleImage, int newWidth, int newHeight) { var kx = grayscaleImage.Width / (double)newWidth; var ky = grayscaleImage.Height / (double)newHeight; var oldBytesPerLine = grayscaleImage.BytesPerLine; var newBytesPerLine = KrecImage.CalculateStride(newWidth, grayscaleImage.Format); var newImageData = new byte[newHeight * newBytesPerLine]; for (var newRowIdx = 0; newRowIdx < newHeight; newRowIdx++) { var oldRowIdx = (int)Math.Round(newRowIdx * ky); if (oldRowIdx == grayscaleImage.Height) { oldRowIdx--; } var oldRowStartIdx = oldRowIdx * oldBytesPerLine; var newRowStartIdx = newRowIdx * newBytesPerLine; for (var newColIdx = 0; newColIdx < newWidth; newColIdx++) { var oldColIdx = (int)Math.Round(kx * newColIdx); if (oldColIdx == grayscaleImage.Width) { --oldColIdx; } newImageData[newRowStartIdx + newColIdx] = grayscaleImage.ImageData[oldRowStartIdx + oldColIdx]; } } return(new KrecImage(newWidth, newHeight, newBytesPerLine, (float)(grayscaleImage.HorizontalResolution / kx), (float)(grayscaleImage.VerticalResolution / ky), grayscaleImage.Format, newImageData)); }
/// <summary> /// Image preparing process. Resizing and fixing quality. /// </summary> /// <param name="originalImage">Original image</param> /// <param name="preComplementWidth">Width used when resizing</param> /// <returns>Grayscaled image for descewing</returns> private static KrecImage PrepareImageForDeskewing(KrecImage originalImage, out int preComplementWidth) { if (originalImage.HorizontalResolution > maxDpi) { // TODO: make k be integer to avoid resampling errors double k = maxDpi / originalImage.HorizontalResolution; originalImage = ImageSizesModifier.CompressImageToNewSizes(originalImage, (int)Math.Round(originalImage.Width * k), (int)Math.Round(originalImage.Height * k)); } KrecImage projectionImage; preComplementWidth = Math.Min(projectionSideWidth, originalImage.Width); if (originalImage.Height < maxCompletedSideWith && originalImage.Width < maxCompletedSideWith) //имитирует поведение прошлого сжимателя { int dx = (maxCompletedSideWith - originalImage.Width) / 2; int dy = (maxCompletedSideWith - originalImage.Height) / 2; int newWidth = projectionSideWidth - dx; int newHeight = projectionSideWidth - dy; double k = Math.Max((double)originalImage.Height / newHeight, (double)originalImage.Width / newWidth); var compressedImage = ImageSizesModifier.CompressImageToNewSizes(originalImage, (int)(originalImage.Width / k), (int)(originalImage.Height / k)); compressedImage = BitmapProcessor.AlignImageBackround(compressedImage); preComplementWidth = (int)(originalImage.Width / k); projectionImage = ImageSizesModifier.GetImageWithPowerOf2Side(compressedImage, projectionSideWidth); } else { var alignedImage = BitmapProcessor.AlignImageBackround(originalImage); projectionImage = ImageSizesModifier.GetImageWithPowerOf2Side(alignedImage, projectionSideWidth); } return(projectionImage); }
/// <summary> /// Print bitmap with array of Boxes in file /// </summary> /// <param name="boxes">Boxes which will be printed.</param> /// <param name="bmp">Bitmap which will be printed.</param> /// <param name="outputPath">Path to print bitmap</param> /// <param name="minPrintHeight">Minimal height of Boxes which will be printed. Null if doesn't metter.</param> /// <param name="maxPrintHeight">Maximal height of Boxes which will be printed. Null if doesn't metter.</param> public static void PrintBoxes(IEnumerable <Rectangle> boxes, Bitmap bmp, string outputPath, int?minPrintHeight = null, int?maxPrintHeight = null) { var image = KrecImage.FromBitmap(bmp); var grayscaledImage = image.ToGrayscaled(); PrintBoxes(boxes, grayscaledImage, outputPath, maxPrintHeight, maxPrintHeight); }
/// <summary> /// Align background of image to white color /// </summary> /// <param name="image">Image to align background</param> /// <returns>Image with aligned bacground</returns> public static KrecImage AlignImageBackround(KrecImage image) { var barGraph = GetColorsBarGraph(image); int threshold = OtsuThreshold(barGraph); int threshold2 = OtsuThreshold(barGraph, 0, threshold); var height = image.Height; var width = image.Width; var bytesPerLine = image.BytesPerLine; var imageData = image.ImageData; for (var rowIdx = 0; rowIdx < height; rowIdx++) { var rowStartIdx = rowIdx * bytesPerLine; for (var colIdx = 0; colIdx < width; colIdx++) { var pixelIdx = rowStartIdx + colIdx; if (imageData[pixelIdx] > threshold2) { imageData[pixelIdx] = 255; } } } return(image); }
/// <summary> /// Find magnitudes of Fourier transfotm and print it in file /// </summary> /// <param name="image">Square image wich sides size is power of 2</param> /// <param name="outputPath">Path to print image</param> public static void PrintFourierMagnitudes(KrecImage image, string outputPath) { int colorsCount = image.Width * image.Height; var resultBytes = new byte[image.Width * image.Height]; var rc = new double[colorsCount]; for (var i = 0; i < colorsCount; ++i) { rc[i] = image.ImageData[i] / 255.0; } rc = SkewAngleDeterminer.FindMagnitudes(rc, image.Width); for (var i = 0; i < colorsCount; ++i) { resultBytes[i] = (byte)Math.Min(255, rc[i] * 10000); } var krecImage = new KrecImage( image.Width, image.Height, image.Width, image.HorizontalResolution, image.VerticalResolution, image.Format, resultBytes); Print8BppImage(krecImage, outputPath); }
/// <summary> /// Print masked bitmap in file /// </summary> /// <param name="bmp">Printed bitmap</param> /// <param name="mask">Mask (true - will be printed)</param> /// <param name="outputPath">Path to print bitmap</param> /// <returns></returns> public static void PrintMaskedBitmap(Bitmap bmp, bool[,] mask, string outputPath) { var image = KrecImage.FromBitmap(bmp); var grayscaledImage = image.ToGrayscaled(); PrintMaskedImage(grayscaledImage, mask, outputPath); }
public KrecImage Apply(KrecImage image, int radius) { var width = image.Width; var height = image.Height; var result = new KrecImage(image, new byte[image.ImageData.Length]); var size = radius * 2 + 1; for (var lineIdx = radius; lineIdx < height - radius; lineIdx++) { for (var counter = radius; counter < width - radius; counter++) { var neighborhood = new List <byte>(); for (var i = 0; i < size; i++) { for (var j = 0; j < size; j++) { var y = lineIdx + i - radius; var sourceIdx = y * image.BytesPerLine + counter; neighborhood.Add(image.ImageData[sourceIdx + j - radius]); } } neighborhood.Sort(); var median = neighborhood[neighborhood.Count / 2]; result.ImageData[lineIdx * image.BytesPerLine + counter] = median; } } return(result); }
private const double maxDpi = 150; // Images of bigger resolution will be compressed /// <summary> /// Get skew angle (in radians) /// </summary> /// <param name="originalBitmap">Bitmap with original image</param> /// <returns>Skew angle (in radians)</returns> public static double GetSkewAngle(Bitmap originalBitmap) { var image = KrecImage.FromBitmap(originalBitmap); var grayscaleImage = image.ToGrayscaled(true); return(GetSkewAngle(grayscaleImage)); }
/// <summary> /// Loads image from file, binarize and rotate it and returns KrecImage /// </summary> /// <param name="path">Image path</param> /// <param name="deskewParameters">The information on how the image has been deskewed</param> /// <returns>Binarized and rotated image with deskew parameters</returns> public static KrecImage LoadAlignedBinarisedImageFromFile(string path, out DeskewParameters deskewParameters) { var sourceImage = KrecImage.FromFile(path); var binarizedImage = sourceImage.Binarize(); deskewParameters = FastImageDeskewer.GetDeskewParameters(binarizedImage); return(RotateGrayscaleImage(binarizedImage, deskewParameters.AngleRadians)); }
private void Save(KrecImage image, string name) { if (!debug) { return; } image.SaveToFile($"{name}.bmp"); }
private KrecImage ReduceImage(Bitmap document) { var width = 300; var height = width * ((double)document.Height / document.Width); dx = (double)document.Width / width; dy = document.Height / height; return(KrecImage.FromBitmap(new Bitmap(document, width, (int)height))); }
public KrecImage Correct(Bitmap document) { var normalizeDocument = ReduceImage(document); var width = normalizeDocument.Width; var height = normalizeDocument.Height; Save(normalizeDocument, "smallDocument"); var sw = Stopwatch.StartNew(); var binaryDocument = normalizeDocument.Binarize(); sw.Stop(); Console.WriteLine($"Binarization, time: {sw.ElapsedMilliseconds} ms"); Save(binaryDocument, "binaryResult"); sw = Stopwatch.StartNew(); var filterDocument = medianFilter.Apply(binaryDocument, 3); filterDocument = medianFilter.Apply(filterDocument, 5); sw.Stop(); Console.WriteLine($"Apply median filter, time: {sw.ElapsedMilliseconds} ms"); Save(filterDocument, "filterResult"); sw = Stopwatch.StartNew(); var sobelX = new[, ] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } }; var sobelY = new[, ] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } }; var documentWithBorders = borderSearcher.Search(filterDocument, sobelX, sobelY); sw.Stop(); Console.WriteLine($"Get points of borders, time: {sw.ElapsedMilliseconds} ms"); Save(documentWithBorders, "bordersResult"); sw = Stopwatch.StartNew(); var lines = equationOfLineFinder.GetLines(borderSearcher.GetPoints(), 4, width, height); sw.Stop(); Console.WriteLine($"Find equations of borders: {sw.ElapsedMilliseconds} ms"); SaveBorders(lines, width, height); sw = Stopwatch.StartNew(); var corners = cornerFinder.FindCorner(lines, width, height); corners = corners.Select(c => new Point((int)Math.Round(c.X * dx), (int)Math.Round(c.Y * dy))).ToList(); sw.Stop(); Console.WriteLine($"Find corners: {sw.ElapsedMilliseconds} ms"); sw = Stopwatch.StartNew(); var correctResult = perspectiveTransformation.ApplyTransformMatrix(KrecImage.FromBitmap(document), corners); sw.Stop(); Console.WriteLine($"Apply transform matrix and smoothing filter: {sw.ElapsedMilliseconds} ms"); return(correctResult); }
/// <summary> /// Get skew angle (in radians) /// </summary> /// <param name="originalImage">Original image to process</param> /// <returns>Skew angle (in radians)</returns> public static double GetSkewAngle(KrecImage originalImage) { int preComplementWidth; var preparedImage = PrepareImageForDeskewing(originalImage, out preComplementWidth); double angle = SkewAngleDeterminer.CalculateAngle(preparedImage); double fixedAngle = FixDocumentOrientation(originalImage, preparedImage, angle, preComplementWidth); return(fixedAngle); }
/// <summary> /// Calculate angle for grayscale image /// </summary> /// <param name="grayImage">Image in grayscale</param> /// <returns>Skew angle</returns> public static double CalculateAngle(KrecImage grayImage) { const double maxError = 0.01; const int brightestPointsCount = 3000; var brightestPoints = GetBrightestPoints(grayImage, brightestPointsCount); double angle = FindAngleByWideBarGraph(maxError, brightestPoints, grayImage.Width); return(CutAngle(angle)); }
/// <summary> /// Return list of the brightest points after Fourier transform /// </summary> /// <param name="grayImage">Image to transform</param> /// <param name="pointsCount">Count of points</param> /// <returns>List of the brightness points</returns> public static List <Point> GetBrightestPoints(KrecImage grayImage, int pointsCount) { var brightestPoints = new List <Point>(); int pixelsCount = grayImage.Width * grayImage.Height; var height = grayImage.Height; var width = grayImage.Width; var bytesPerLine = grayImage.BytesPerLine; var arrayOfMagnitudes = new double[pixelsCount]; var targetIdx = 0; for (var rowIdx = 0; rowIdx < height; rowIdx++) { var rowStartIdx = rowIdx * bytesPerLine; for (var colIdx = 0; colIdx < width; colIdx++) { arrayOfMagnitudes[targetIdx] = grayImage.ImageData[rowStartIdx + colIdx] / 255.0; targetIdx++; } } arrayOfMagnitudes = FindMagnitudes(arrayOfMagnitudes, grayImage.Width); var indexesForNorm = new Tuple <int, double> [pixelsCount]; for (var i = 0; i < pixelsCount; ++i) { indexesForNorm[i] = new Tuple <int, double>(i, arrayOfMagnitudes[i]); } var maxMagnitude = FindKthOrderStatistic(indexesForNorm, pointsCount, true); indexesForNorm = indexesForNorm.Skip(pixelsCount - pointsCount).ToArray(); if (maxMagnitude < 0.00001) { throw new Exception("Monotone image, can't determine scew angle"); } foreach (var index in indexesForNorm) { int x = index.Item1 % grayImage.Width; int y = index.Item1 / grayImage.Width; brightestPoints.Add(new Point(x, y)); } if (brightestPoints.Count == 0) { throw new Exception("Empty brightest points"); } return(brightestPoints); }
/// <summary> /// Print image with 24bppRgb pixel format /// </summary> /// <param name="image">Image with 24bppRgb pixel format</param> /// <param name="outputPath">Path to print image</param> public static void Print24BppImage(KrecImage image, string outputPath) { var bmp = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb); bmp.SetResolution(image.HorizontalResolution, image.VerticalResolution); bmp.WithBitmapData(bitmapData => { var rgbValues = BitmapProcessor.Reconstruct24To24BppRgbBitmap(bitmapData, image.ImageData); IntPtr ptr = bitmapData.Scan0; Marshal.Copy(rgbValues, 0, ptr, rgbValues.Length); }); bmp.Save(outputPath); }
/// <summary> /// Get angle to right image orientation (Multiples Pi/2, from -Pi/2 to Pi) /// </summary> /// <param name="image">Deskewed mage which nedd to be oriented</param> /// <returns>Angle to right orientation</returns> public static double GetAngleToRightOrientation(KrecImage image) { var binary = BitmapProcessor.BinarizeGrayscaleImage(image); var result90 = Get90AngleToRightOrientation(binary, image.HorizontalResolution, image.VerticalResolution); bool isCorrect90Oriented = result90.Item1; var lines = result90.Item2.Lines; // Not enough information to perform calculations of rotation angle // All lines contains less than one char inside if (lines.All(line => line.Chars.Length <= 1)) { return(0); } if (!isCorrect90Oriented) { binary = RotateMatrixBy90(binary); } CleanUpBinaryFromNoise(binary, result90.Item2.BadChars); var linesSum = GetBluredBoundingRectsSums(lines.Select(line => line.BoundingRect), binary); double orientSum = 0; for (var i = 0; i < linesSum.Count; ++i) { orientSum += GetLineOrientation(lines[i].BoundingRect, binary, linesSum[i], result90.Item2.MaxCharHeight); } double expectation = orientSum / linesSum.Count; bool isCorrect180Oriented = (Math.Abs(expectation) < 0.011) || orientSum >= 0; //при мат. ожидании меньше 0.011 результаты данной оценки недостоверны, а статистически вероятнее всего изображение ориентировано правильно if (!isCorrect180Oriented && !isCorrect90Oriented) { return(-Math.PI / 2); } if (!isCorrect180Oriented) { return(Math.PI); } if (!isCorrect90Oriented) { return(Math.PI / 2); } return(0); }
/// <summary> /// Print bitmap with array of Boxes in file /// </summary> /// <param name="boxes">Boxes which will be printed.</param> /// <param name="grayscaleImage">Image which will be printed.</param> /// <param name="outputPath">Path to print image</param> /// <param name="minPrintHeight">Minimal height of Boxes which will be printed. Null if doesn't metter.</param> /// <param name="maxPrintHeight">Maximal height of Boxes which will be printed. Null if doesn't metter.</param> public static void PrintBoxes(IEnumerable <Rectangle> boxes, KrecImage grayscaleImage, string outputPath, int?minPrintHeight = null, int?maxPrintHeight = null) { var newWidth = grayscaleImage.Width; var newHeight = grayscaleImage.Height; var oldBytesPerLine = grayscaleImage.BytesPerLine; var newBytesPerLine = KrecImage.CalculateStride(newWidth, KrecImagePixelFormat.Format24bppRgb); var newImageData = new byte[newBytesPerLine * newHeight * 3]; for (int rowIdx = 0; rowIdx < newHeight; rowIdx++) { var oldRowStartIdx = rowIdx * oldBytesPerLine; var newRowStartIdx = rowIdx * newBytesPerLine; for (int colIdx = 0; colIdx < newWidth; colIdx++) { int ix = newRowStartIdx + colIdx * 3; newImageData[ix + 2] = newImageData[ix + 1] = newImageData[ix] = grayscaleImage.ImageData[oldRowStartIdx + colIdx]; } } int boxCounter = 0; foreach (var box in boxes) { if ((maxPrintHeight != null && box.Height > maxPrintHeight) || (minPrintHeight != null && box.Height < minPrintHeight)) { continue; } for (var i = box.Y; i < box.Y + box.Height && i < grayscaleImage.Height; ++i) { for (var j = box.X; j < box.X + box.Width && j < grayscaleImage.Width; ++j) { int pixelIdx = (i * newBytesPerLine + j) * 3; newImageData[pixelIdx] = 255; newImageData[pixelIdx + 1] = (byte)((boxCounter % 2) * 255); newImageData[pixelIdx + 2] = 0; } } ++boxCounter; } var krecImage = new KrecImage( newWidth, newHeight, newBytesPerLine, grayscaleImage.HorizontalResolution, grayscaleImage.VerticalResolution, KrecImagePixelFormat.Format24bppRgb, newImageData); Print24BppImage(krecImage, outputPath); }
/// <summary> /// Find magnitudes of Fourier transfotm and print it in file /// </summary> /// <param name="image">Square image wich sides size is power of 2</param> /// <param name="outputPath">Path to print image</param> public static void PrintFourierBrightestPoints(KrecImage image, string outputPath) { var resultBytes = new byte[image.Width * image.Height]; var brightestPoints = SkewAngleDeterminer.GetBrightestPoints(image, 3000); foreach (var point in brightestPoints) { resultBytes[point.Y * image.Width + point.X] = 255; } var krecImage = new KrecImage( image.Width, image.Height, image.Width, image.HorizontalResolution, image.VerticalResolution, image.Format, resultBytes); Print8BppImage(krecImage, outputPath); }
/// <summary> /// Builds integral matrix to claculate sums by given rectangular range /// </summary> /// <param name="image"></param> /// <returns></returns> private static uint[,] GetIntegralCopy(KrecImage image) { var width = image.Width; var height = image.Height; var bytesPerLine = image.BytesPerLine; var imageData = image.ImageData; var numArray = new uint[height + 1, width + 1]; for (var y = 0; y < height; y++) { uint lineCurrentSum = 0U; for (var x = 0; x < width; x++) { lineCurrentSum += imageData[y * bytesPerLine + x]; numArray[y + 1, x + 1] = numArray[y, x + 1] + lineCurrentSum; } } return(numArray); }
/// <summary> /// Complete images to power of 2 sizes by white lines. If images width or height more then maxSideWith then compress image. /// </summary> /// <param name="originalImage">Image to complete</param> /// <param name="maxSideWidth">Max completion side width</param> /// <returns>Completed image</returns> public static KrecImage GetImageWithPowerOf2Side(KrecImage originalImage, int maxSideWidth) { var newWidth = Math.Max(originalImage.Width, originalImage.Height); if ((newWidth & (newWidth - 1)) != 0) // the fasterst way to check that the number is a power of two { var exp = (int)Math.Log(newWidth, 2); newWidth = (int)Math.Pow(2, exp + 1); } newWidth = Math.Min(newWidth, maxSideWidth); if (newWidth == originalImage.Width && newWidth == originalImage.Height) { return(originalImage); } return(GetCompletedToSizeGrayscaleImage(originalImage, newWidth, newWidth)); }
/// <summary> /// Convert image to binary format /// </summary> /// <param name="grayscaleImage">Grayscale image</param> /// <returns>0 - white, 1 - black</returns> public static bool[,] BinarizeGrayscaleImage(KrecImage grayscaleImage) { var threshold = OtsuThreshold(grayscaleImage); var binary = new bool[grayscaleImage.Width, grayscaleImage.Height]; var height = grayscaleImage.Height; var width = grayscaleImage.Width; var bytesPerLine = grayscaleImage.BytesPerLine; var imageData = grayscaleImage.ImageData; for (var rowIdx = 0; rowIdx < height; rowIdx++) { var rowStartIdx = rowIdx * bytesPerLine; for (int colIdx = 0; colIdx < width; colIdx++) { binary[colIdx, rowIdx] = imageData[rowStartIdx + colIdx] <= threshold; } } return(binary); }
/// <summary> /// Get bar grapgh for colors in greyscale /// </summary> /// <param name="grayscaledImage">Greyscaled image to process</param> /// <returns>Bar graph</returns> public static int[] GetColorsBarGraph(KrecImage grayscaledImage) { var barGraph = new int[256]; //в массиве будет не более 256 элементов var width = grayscaledImage.Width; var height = grayscaledImage.Height; var bytesPerLine = grayscaledImage.BytesPerLine; var imageData = grayscaledImage.ImageData; for (var rowIdx = 0; rowIdx < height; rowIdx++) { var rowStartIdx = rowIdx * bytesPerLine; for (var colIdx = 0; colIdx < width; colIdx++) { barGraph[imageData[rowStartIdx + colIdx]]++; } } return(barGraph); }
/// <summary> /// Print masked bitmap in file /// </summary> /// <param name="image">Printed image</param> /// <param name="mask">Mask (true - will be printed)</param> /// <param name="outputPath">Path to print image</param> /// <returns></returns> public static void PrintMaskedImage(KrecImage image, bool[,] mask, string outputPath) { var resultBytes = new byte[image.Width * image.Height]; for (var y = 0; y < image.Height; ++y) { for (var x = 0; x < image.Width; ++x) { if (!mask[x, y]) { resultBytes[y * image.BytesPerLine + x] = 255; } } } var krecImage = new KrecImage( image.Width, image.Height, image.Width, image.HorizontalResolution, image.VerticalResolution, image.Format, resultBytes); Print8BppImage(krecImage, outputPath); }
private byte[] GetСolor(double[,] doublePoint, KrecImage image, int bytesPerPixel) { var doublePointX = doublePoint[0, 0] / doublePoint[0, 2]; var doublePointY = doublePoint[0, 1] / doublePoint[0, 2]; var neighboringPoints = new [] { new Point((int)Math.Floor(doublePointX), (int)Math.Floor(doublePointY)), new Point((int)Math.Floor(doublePointX), (int)Math.Ceiling(doublePointY)), new Point((int)Math.Ceiling(doublePointX), (int)Math.Floor(doublePointY)), new Point((int)Math.Ceiling(doublePointX), (int)Math.Ceiling(doublePointY)) }; var distanceToNeighbors = neighboringPoints.Select(p => Math.Sqrt((doublePointX - p.X) * (doublePointX - p.X) + (doublePointY - p.Y) * (doublePointY - p.Y))).ToArray(); var sumOfDistances = distanceToNeighbors.Sum(); var weightNeighbors = new double[4]; for (var i = 0; i < distanceToNeighbors.Length; i++) { weightNeighbors[i] = 0.5 - distanceToNeighbors[i] / sumOfDistances; } var color = new byte[bytesPerPixel]; for (var pointIndex = 0; pointIndex < neighboringPoints.Length; pointIndex++) { var neighboringPoint = neighboringPoints[pointIndex]; if (neighboringPoint.X > 0 && neighboringPoint.X < image.Width && neighboringPoint.Y > 0 && neighboringPoint.Y < image.Height) { var index = neighboringPoint.Y * image.BytesPerLine + neighboringPoint.X * bytesPerPixel; for (var i = 0; i < bytesPerPixel; i++) { color[i] += (byte)(image.ImageData[index + i] * weightNeighbors[pointIndex]); } } } return(color); }
public KrecImage Search(KrecImage image, int[,] maskX, int[,] maskY) { var width = image.Width; var height = image.Height; var result = new KrecImage(image, new byte[image.ImageData.Length]); const int limit = 128 * 128; for (var lineIdx = 1; lineIdx < height - 1; lineIdx++) { for (var counter = 1; counter < width - 1; counter++) { var gX = 0; var gY = 0; for (var i = 0; i < 3; i++) { var y = lineIdx + i - 1; var sourceIdx = y * image.BytesPerLine + counter; for (var j = 0; j < 3; j++) { var pixel = image.ImageData[sourceIdx + j - 1]; gX += maskX[i, j] * pixel; gY += maskY[i, j] * pixel; } } byte color = 0; if (gX * gX + gY * gY > limit) { color = 255; borderPixels.Add(new Point(counter, lineIdx)); } result.ImageData[lineIdx * image.BytesPerLine + counter] = color; } } return(result); }
/// <summary> /// Print image with 8bppIndexed pixel format (before printing convert it to 24bpp) /// </summary> /// <param name="image">Image with 8bppIndexed pixel format</param> /// <param name="outputPath">Path to print image</param> public static void Print8BppImage(KrecImage image, string outputPath) { var bmp = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb); bmp.SetResolution(image.HorizontalResolution, image.VerticalResolution); int len = image.Width * image.Height * 3; var result = new byte[len]; for (var i = 0; i < len; i += 3) { int ind = i / 3; result[i] = result[i + 1] = result[i + 2] = image.ImageData[ind]; } bmp.WithBitmapData(bitmapData => { var grayscaleValues = BitmapProcessor.Reconstruct24To24BppRgbBitmap(bitmapData, result); IntPtr ptr = bitmapData.Scan0; Marshal.Copy(grayscaleValues, 0, ptr, grayscaleValues.Length); }); bmp.Save(outputPath); }
/// <summary> /// Fixing orientiation before returning scew angle /// </summary> /// <param name="originalImage">Original image</param> /// <param name="preparedImage">Prepared for deskewing image</param> /// <param name="angle">Calculated scew angle</param> /// <param name="preComplementWidth">Width used when resizing</param> /// <returns>Fixed descew angle</returns> private static double FixDocumentOrientation(KrecImage originalImage, KrecImage preparedImage, double angle, int preComplementWidth) { var deskewedImage = BitmapProcessor.RotateGrayscaleImage(originalImage, -angle); // TODO: бинаризацию можно сделать в этой точке, а не выполнять ее дважды в // TODO: GetImageBordersWithoutWhiteSpaces и GetAngleToRightOrientation var borders = GetImageBordersWithoutWhiteSpaces(angle, preComplementWidth, deskewedImage.Width, deskewedImage.Height, preparedImage, originalImage.Width, originalImage.Height); var imagePart = deskewedImage.GetSubregion(borders); var orientationResult = OrientationDeterminer.GetAngleToRightOrientation(imagePart); double orientAngle = orientationResult; double result = angle - orientAngle; if (result > Math.PI) { result -= 2 * Math.PI; } else if (result < -Math.PI) { result += 2 * Math.PI; } return(result); }