- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我一直在寻找一种可靠的方法来校正 .Net 中的图像,但运气不佳。
目前我正在使用 Aforge。这是一个痛苦,因为我正在使用 WPF,所以我正在使用的图像是 BitmapImage 对象,而不是 Bitmap 对象,这意味着我需要从 BitmapImage 对象开始,将其保存到内存流,创建一个新的 Bitmap 对象从内存流中,进行去歪斜过程,将去歪斜图像保存到新的内存流中,然后从所述内存流创建一个新的 BitmapImage 对象。不仅如此,偏移校正效果也不是很好。
我正在尝试读取扫描到扫描仪中的一张纸的 OMR 数据,因此我需要依赖每次都处于相同坐标的特定 OMR 框,因此倾斜校正需要可靠。
所以我现在正在使用 Aforge,我在 .Net 中找不到任何其他用于图像倾斜校正的免费/开源库,我发现的所有内容要么相当昂贵,要么采用 C/C++ 编写。
我的问题是是否存在其他免费/开源库可以帮助 .Net 中的图像校正?如果是的话,它们叫什么,如果不是,我应该如何解决这个问题?
编辑:例如,假设我有以下页面:
注意:这仅用于说明目的,但实际图像确实在页面的每个角都有一个黑色矩形,也许这会有所帮助。
当我打印出来并将其扫描回扫描仪时,它看起来像这样:
我需要校正该图像,以便我的盒子每次都位于同一个位置。在现实世界中,有很多盒子,它们较小且靠得很近,因此准确性很重要。
我目前的方法是一个非常无效的痛苦方法:
using AForge.Imaging;
using AForge.Imaging.Filters;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Media.Imaging;
public static BitmapImage DeskewBitmap(BitmapImage skewedBitmap)
{
//Using a memory stream to minimise disk IO
var memoryStream = BitmapImageToMemoryStream(skewedBitmap);
var bitmap = MemoryStreamToBitmap(memoryStream);
var skewAngle = CalculateSkewAngle(bitmap);
//Aforge needs a Bppp indexed image for the deskewing process
var bitmapConvertedToBbppIndexed = ConvertBitmapToBbppIndexed(bitmap);
var rotatedImage = DeskewBitmap(skewAngle, bitmapConvertedToBbppIndexed);
//I need to convert the image back to a non indexed format to put it back into a BitmapImage object
var imageConvertedToNonIndexed = ConvertImageToNonIndexed(rotatedImage);
var imageAsMemoryStream = BitmapToMemoryStream(imageConvertedToNonIndexed);
var memoryStreamAsBitmapImage = MemoryStreamToBitmapImage(imageAsMemoryStream);
return memoryStreamAsBitmapImage;
}
private static Bitmap ConvertImageToNonIndexed(Bitmap rotatedImage)
{
var imageConvertedToNonIndexed = rotatedImage.Clone(
new Rectangle(0, 0, rotatedImage.Width, rotatedImage.Height), PixelFormat.Format32bppArgb);
return imageConvertedToNonIndexed;
}
private static Bitmap DeskewBitmap(double skewAngle, Bitmap bitmapConvertedToBbppIndexed)
{
var rotationFilter = new RotateBilinear(-skewAngle) { FillColor = Color.White };
var rotatedImage = rotationFilter.Apply(bitmapConvertedToBbppIndexed);
return rotatedImage;
}
private static double CalculateSkewAngle(Bitmap bitmapConvertedToBbppIndexed)
{
var documentSkewChecker = new DocumentSkewChecker();
double skewAngle = documentSkewChecker.GetSkewAngle(bitmapConvertedToBbppIndexed);
return skewAngle;
}
private static Bitmap ConvertBitmapToBbppIndexed(Bitmap bitmap)
{
var bitmapConvertedToBbppIndexed = bitmap.Clone(
new Rectangle(0, 0, bitmap.Width, bitmap.Height), PixelFormat.Format8bppIndexed);
return bitmapConvertedToBbppIndexed;
}
private static BitmapImage ResizeBitmap(BitmapImage originalBitmap, int desiredWidth, int desiredHeight)
{
var ms = BitmapImageToMemoryStream(originalBitmap);
ms.Position = 0;
var result = new BitmapImage();
result.BeginInit();
result.DecodePixelHeight = desiredHeight;
result.DecodePixelWidth = desiredWidth;
result.StreamSource = ms;
result.CacheOption = BitmapCacheOption.OnLoad;
result.EndInit();
result.Freeze();
return result;
}
private static MemoryStream BitmapImageToMemoryStream(BitmapImage image)
{
var ms = new MemoryStream();
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(ms);
return ms;
}
private static BitmapImage MemoryStreamToBitmapImage(MemoryStream ms)
{
ms.Position = 0;
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = ms;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
private static Bitmap MemoryStreamToBitmap(MemoryStream ms)
{
return new Bitmap(ms);
}
private static MemoryStream BitmapToMemoryStream(Bitmap image)
{
var memoryStream = new MemoryStream();
image.Save(memoryStream, ImageFormat.Bmp);
return memoryStream;
}
回想起来,还有几个问题:
最佳答案
根据示例输入,很明显您并不是在进行图像倾斜校正。这种操作不会纠正您所拥有的扭曲,而是需要执行透视变换。从下图中可以清楚地看到这一点。四个白色矩形代表四个黑盒子的边缘,黄线是连接黑盒子的结果。黄色四边形不是倾斜的红色四边形(您想要实现的四边形)。
所以,如果你真的能得到上图,问题就变得简单很多了。如果你没有四个角框,你将需要其他四个引用点,所以它们确实对你有很大帮助。获得上图后,您就知道了四个黄色角,然后只需将它们映射到四个红色角即可。这是您需要执行的透视变换,根据您的库,可能有一个现成的函数(至少有一个,请检查对您问题的评论)。
获取上图的方法有很多种,我只介绍一种相对简单的方法。首先,对灰度图像进行二值化。为此,我选择了一个简单的全局阈值 100(您的图像在 [0, 255] 范围内),它保留了图像中的框和其他细节(例如图像周围的粗线)。高于或等于 100 的强度设置为 255,低于 100 的强度设置为 0。但是,由于这是打印图像,因此框显示的暗度很可能会有所不同。因此,您可能需要一种更好的方法,像形态梯度这样简单的方法可能效果更好。第二步是消除不相关的细节。为此,请使用 7x7 正方形(输入图像的宽度和高度之间的最小值的大约 1%)执行形态学闭运算。要获取框的边界,请使用形态侵 eclipse ,如使用基本 3x3 正方形的 current_image - 侵 eclipse (current_image)
中所示。现在你有了一个带有上面四个白色轮廓的图像(这是假设除了盒子之外的所有内容都被消除了,我相信是其他输入的简化)。要获取这些白色轮廓的像素,您可以进行连通分量标记。通过这 4 个组件,确定右上一个、左上一个、右下一个和左下一个。现在您可以轻松找到所需的点来获取黄色矩形的角点。所有这些操作都可以在 AForge 中轻松使用,因此只需将以下代码翻译为 C# 即可:
import sys
import numpy
from PIL import Image, ImageOps, ImageDraw
from scipy.ndimage import morphology, label
# Read input image and convert to grayscale (if it is not yet).
orig = Image.open(sys.argv[1])
img = ImageOps.grayscale(orig)
# Convert PIL image to numpy array (minor implementation detail).
im = numpy.array(img)
# Binarize.
im[im < 100] = 0
im[im >= 100] = 255
# Eliminate undesidered details.
im = morphology.grey_closing(im, (7, 7))
# Border of boxes.
im = im - morphology.grey_erosion(im, (3, 3))
# Find the boxes by labeling them as connected components.
lbl, amount = label(im)
box = []
for i in range(1, amount + 1):
py, px = numpy.nonzero(lbl == i) # Points in this connected component.
# Corners of the boxes.
box.append((px.min(), px.max(), py.min(), py.max()))
box = sorted(box)
# Now the first two elements in the box list contains the
# two left-most boxes, and the other two are the right-most
# boxes. It remains to stablish which ones are at top,
# and which at bottom.
top = []
bottom = []
for index in [0, 2]:
if box[index][2] > box[index+1][2]:
top.append(box[index + 1])
bottom.append(box[index])
else:
top.append(box[index])
bottom.append(box[index + 1])
# Pick the top left corner, top right corner,
# bottom right corner, and bottom left corner.
reference_corners = [
(top[0][0], top[0][2]), (top[1][1], top[1][2]),
(bottom[1][1], bottom[1][3]), (bottom[0][0], bottom[0][3])]
# Convert the image back to PIL (minor implementation detail).
img = Image.fromarray(im)
# Draw lines connecting the reference_corners for visualization purposes.
visual = img.convert('RGB')
draw = ImageDraw.Draw(visual)
draw.line(reference_corners + [reference_corners[0]], fill='yellow')
visual.save(sys.argv[2])
# Map the current quadrilateral to an axis-aligned rectangle.
min_x = min(x for x, y in reference_corners)
max_x = max(x for x, y in reference_corners)
min_y = min(y for x, y in reference_corners)
max_y = max(y for x, y in reference_corners)
# The red rectangle.
perfect_rect = [(min_x, min_y), (max_x, min_y), (max_x, max_y), (min_x, max_y)]
# Use these points to do the perspective transform.
print reference_corners
print perfect_rect
上面的代码与您的输入图像的最终输出是:
[(55, 30), (734, 26), (747, 1045), (41, 1036)]
[(41, 26), (747, 26), (747, 1045), (41, 1045)]
第一个点列表描述了黄色矩形的四个角,第二个点列表与红色矩形相关。要进行透视变换,您可以使用带有ready函数的AForge。为了简单起见,我使用 ImageMagick,如下所示:
convert input.png -distort Perspective "55,30,41,26 734,26,747,26 747,1045,747,1045 41,1036,41,1045" result.png
这给出了您想要的对齐方式(与之前一样找到蓝线以更好地显示结果):
您可能会注意到左侧垂直蓝线并不完全笔直,事实上最左侧的两个框在 x 轴上未对齐 1 个像素。这可以通过透视变换期间使用的不同插值来纠正。
关于.net - 使用 .Net 校正图像歪斜,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14156498/
创建使用.NET框架的asp.net页面时,访问该页面的客户端是否需要在其计算机上安装.NET框架? IE。用户访问www.fakesite.com/default.aspx,如果他们没有安装框架,他
我阅读了很多不同的博客和 StackOverflow 问题,试图找到我的问题的答案,但最后我找不到任何东西,所以我想自己问这个问题。 我正在构建一个应用程序,其中有一个长时间运行的工作线程,它执行一些
已锁定。这个问题及其答案是locked因为这个问题是题外话,但却具有历史意义。目前不接受新的答案或互动。 我一直想知道为什么微软为这样一个伟大的平台选择了一个如此奇怪的、对搜索引擎不友好的名称。他们就
.Net Framework .Net .NET Standard的区别 1、.NET Framework 在未来.NET Framework或许成为过去时,目前还是有很多地方在使用的。这一套
如果有选择的话,您会走哪条路? ASP.NET Webforms + ASP.NET AJAX 或 ASP.NET MVC + JavaScript Framework of your Choice
我有一个 Web 服务,它通过专用连接通过 https 使用第三方 Web 服务,我应用了 ServicePointManager.ServerCertificateValidationCallbac
为什么我应该选择ASP.NET Web Application (.NET Framework)而不是ASP.NET Core Web Application (.NET Framework)? 我在
我在网络上没有找到任何关于包含 .NET Standard、.NET Core 和 .NET Framework 项目的 .NET 解决方案的公认命名约定。 就我而言,我们在 .NET 框架项目中有以
.NET Compact 是 .NET 的完美子集吗? 假设我考虑了屏幕大小和其他限制并避免了 .NET Compact 不支持的类和方法,或者 .NET Compact 是一个不同且不兼容的 GUI
我已经阅读了所有我能找到的关于 connectionManagement 中的 maxconnection 设置的文章:即 http://support.microsoft.com/kb/821268
我现在正在使用asp.net mvc,想知道使用内置的Json或 Json.Net哪个是更好的选择,但我不确定一个人是否比另一个人有优势。 另外,如果我确实选择沿用Json.Net的路线,那么我应该选
在 Visual Studio 中,您至少可以创建三种不同类型的类库: 类库(.NET Framework) 类库(.NET 标准) 类库(.NET Core) 虽然第一个是我们多年来一直使用的,但我
.NET 和 ASP.NET 之间有什么区别?它们有什么关系? 最佳答案 ASP.Net 基于 .Net 框架构建,提供有关 Web 开发的附加功能。 你可以去看看wikipedia article
在安装更高版本(3.0)之前,我需要安装.net框架1.1和2.0吗?或者单独安装 3.0 框架就足够了,并为在早期框架版本上编写的软件提供支持?谢谢 ,丽然 最佳答案 不,您不必安装以前的框架。 我
我正在开发一个项目,人们可以“更新”类别,例如更改类别的名称。我收到以下消息 This is called after clicking update 按钮 with the SQL statemen
.NET 类 System.Net.CookieContainer 线程安全吗? --更新:交 key 答复-- 是否有任何方法可以确保异步请求期间修改的变量(即 HttpWebRequest.Coo
我正在使用 JScript.NET 在我编写的 C# WinForms 应用程序中编写脚本。它工作得很好,但我只是尝试在脚本中放置一些异常处理,但我无法弄清楚如何判断我的 C# 代码抛出了哪种类型的异
我需要你的帮助, 比如我有一个小数类型的变量,我想这样取整。 例如 3.0 = 3 3.1 = 4 3.2 = 4 3.3 = 4 3.4 = 4 3.5 = 4 3.6 = 4 3.7 = 4 3.
我使用过这样的代码:http://msdn.microsoft.com/en-us/library/dw70f090.aspx在 ASP.NET 中工作之前访问数据库(2-3 年前)。我没有意识到我正
自 ConfigurationManager .NET Standard 中不存在,检索正在执行的程序集的应用程序设置的最佳方法是什么,无论是 web.config或 appSettings.{env
我是一名优秀的程序员,十分优秀!