public double GetSkewAngle(Bitmap image)
        {
            var bitArrays = UnsafeImageOps.ConvertToBitArrays(image);

            var h = image.Height;
            var w = image.Width;
            // Ignore a bit of the top/bottom so that artifacts from the edge
            // of the scan area aren't counted. (Left/right don't matter since
            // we only look at near-horizontal lines.)
            var yOffset = (int)Math.Round(h * IGNORE_EDGE_FRACTION);

            var sinCos = PrecalculateSinCos();

            // TODO: Consider reducing the precision of distance or angle,
            // TODO: possibly with a second pass to restore precision.
            var dCount = 2 * (w + h);
            var scores = new int[dCount, ANGLE_STEPS];

            // TODO: This should be a good candidate for OpenCL optimization.
            // TODO: If you parallelize over the angle, you're operating over
            // TODO: the same input data with the same branches.
            for (var y = 1 + yOffset; y <= h - 2 - yOffset; y++)
            {
                for (var x = 1; x <= w - 2; x++)
                {
                    if (!bitArrays[y][x] || bitArrays[y + 1][x])
                    {
                        continue;
                    }
                    for (var i = 0; i < ANGLE_STEPS; i++)
                    {
                        var sc = sinCos[i];
                        var d  = (int)(y * sc.cos - x * sc.sin + w);
                        scores[d, i]++;
                    }
                }
            }

            var angles  = GetAnglesOfBestLines(scores, dCount);
            var cluster = ClusterAngles(angles);

            if (cluster.Length < angles.Length / 2)
            {
                // Could not find a consistent skew angle
                return(0);
            }
            return(cluster.Sum() / cluster.Length);
        }
Exemple #2
0
        private async Task <(ScannedImage, bool)> Transfer(Lazy <KeyValueScanOptions> options, int pageNumber)
        {
            return(await Task.Factory.StartNew(() =>
            {
                Stream stream;
                if (ScanParams.NoUi)
                {
                    stream = saneWrapper.ScanOne(ScanDevice.Id, options.Value, null, CancelToken);
                }
                else
                {
                    var form = formFactory.Create <FScanProgress>();
                    var unifiedCancelToken = CancellationTokenSource.CreateLinkedTokenSource(form.CancelToken, CancelToken).Token;
                    form.Transfer = () => saneWrapper.ScanOne(ScanDevice.Id, options.Value, form.OnProgress, unifiedCancelToken);
                    form.PageNumber = pageNumber;
                    ((FormBase)Application.OpenForms[0]).SafeInvoke(() => form.ShowDialog());

                    if (form.Exception != null)
                    {
                        form.Exception.PreserveStackTrace();
                        throw form.Exception;
                    }
                    if (form.DialogResult == DialogResult.Cancel)
                    {
                        return (null, true);
                    }

                    stream = form.ImageStream;
                }
                if (stream == null)
                {
                    return (null, true);
                }
                using (stream)
                    using (var output = Image.FromStream(stream))
                        using (var result = scannedImageHelper.PostProcessStep1(output, ScanProfile, false))
                        {
                            if (blankDetector.ExcludePage(result, ScanProfile))
                            {
                                return (null, false);
                            }

                            // By converting to 1bpp here we avoid the Win32 call in the BitmapHelper conversion
                            // This converter also has the side effect of working even if the scanner doesn't support Lineart
                            using (var encoded = ScanProfile.BitDepth == ScanBitDepth.BlackWhite ? UnsafeImageOps.ConvertTo1Bpp(result, -ScanProfile.Brightness) : result)
                            {
                                var image = new ScannedImage(encoded, ScanProfile.BitDepth, ScanProfile.MaxQuality, ScanProfile.Quality);
                                scannedImageHelper.PostProcessStep2(image, result, ScanProfile, ScanParams, 1, false);
                                var tempPath = scannedImageHelper.SaveForBackgroundOcr(result, ScanParams);
                                scannedImageHelper.RunBackgroundOcr(image, ScanParams, tempPath);
                                return (image, false);
                            }
                        }
            }, TaskCreationOptions.LongRunning));
        }