计算机视觉与模式识别(2)—— A4纸矫正

上次写了A4纸的边缘提取,发现我的代码还是存在着很多的问题,比如令人诟病的静态阈值,还有非结构化的编程风格。于是我重新整理了一下,把A4纸边缘提取的代码整合为一个类。不过那个该死的阈值啊,我暂时还没有找到完美的方法,使得适用于所有的图像_(:з」∠)_。

优化的方法倒是有一点,那就是降低标准,择优录取。也就是把阈值调得很低,但是峰值提取的结果只取最优的4个。当然啦,这种方法偶尔会取到奇怪的边缘,而且由于阈值的降低,导致的计算量也成倍增长,特别是Hough变换。但综合来看,鲁棒性还是增强了不少。

另外,大家可以搜下有关 “边缘提取动态阈值获取” 的论文。梯度阈值动态的一个简单方法,就是取所有像素点梯度的平均值,至于效果怎样,有待大家尝试哦~

那么,在上一节的基础上,我们还能做什么呢?

我们现在只是知道了A4纸的边缘和角点,每一张A4纸都处于不同的的角度、位置,甚至有着不同的形状。这些“畸形”的A4纸不利于我们进一步的图像处理,因此需要把它们矫正成统一的矩形。

输入图像:

普通A4打印纸,上面可能有手写笔记或者打印内容,但是拍照时可能角度不正。

        

输出图像:

已经矫正好的标准普通A4纸(210:297),并裁掉无用的其他内容,只保留完整的A4纸张。

        

实验中,我使用了两种方法,一种是Projective Transform(仿射变换),一种是Morphing(变形)。

两种方法各有优缺点,其中仿射变换处理速度快,矫正图片准确,但是涉及矩阵演算。Morphing的图像准确度没有仿射变换高,但是原理通俗易懂,不怎么需要演算。

方法一:Projective Transform

主要参考这篇博客:http://blog.csdn.net/xiaowei_cqu/article/details/26471527

但是有几个错误需要指出来,不为0时,应得到:

a11= x1 - x0 +a13* x1             a12= y1 - y0 +a13* y1        a13

a21
= x3 - x0 + a23 * x3           a22= y3 - y0 +a23* y3        a23

a31
= x0                                   a32 = y0                            a33 = 1

关键代码:

Matrix3x3 A4ShapeCorrect::squareToQuadrilateral(double x0, double y0, double x1, double y1, double x2,
	double y2, double x3, double y3)
{
	double dx3 = x0 - x1 + x2 - x3;
	double dy3 = y0 - y1 + y2 - y3;
	if (dx3 == 0.0f && dy3 == 0.0f)
	{
		Matrix3x3 result(x1 - x0, y1 - y0, 0,
				 x2 - x1, y2 - y1, 0,
				 x0, y0, 1);
		return result;
	}
	else
	{
		double dx1 = x1 - x2;
		double dx2 = x3 - x2;
		double dy1 = y1 - y2;
		double dy2 = y3 - y2;
		double denominator = dx1 * dy2 - dx2 * dy1;
		double a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
		double a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
		Matrix3x3 result(x1 - x0 + a13 * x1, y1 - y0 + a13*y1, a13,
				 x3 - x0 + a23*x3, y3 - y0 + a23*y3, a23,
				 x0, y0, 1);
		return result;
	}
}

计算出了变换矩阵的系数后,只需要应用到每个像素坐标就好了。要注意方向:从目标像素映射到原像素,并且计算插值。

Matrix3x3 H = squareToQuadrilateral(dots[0]->x, dots[0]->y, dots[1]->x, dots[1]->y,
				    dots[2]->x, dots[2]->y, dots[3]->x, dots[3]->y);

/* Method 1: Projective Transforming */
cimg_forXY(*target, x, y)
{
	double _x = (double)x / _width;
	double _y = (double)y / _height;
	double denominator = H.a13 * _x + H.a23 * _y + H.a33;
	double tx = (H.a11 * _x + H.a21 * _y + H.a31) / denominator;
	double ty = (H.a12 * _x + H.a22 * _y + H.a32) / denominator;
	cimg_forC(*target, c)
		(*target)(x, y, 0, c) = bilinear(img, tx, ty, 0, c);
}

方法二:Morphing

针对三角形进行Morph变换,思路类似双线性插值:

如何计算P‘的坐标::DA / BA = D‘A‘ / B‘A‘  , PC / DC = P‘C‘ / D‘C‘, 其中ABC、A‘B‘C‘和P坐标已知。

有了计算变换三角形位置的方法,我们就可以把原A4纸延对角线切割成两个三角形,把目标图形也延对角线切割成两个三角形,然后分别对这两个三角形内的像素点坐标做Morph变换即可。

其中还要解决的问题是如何判断一个点在三角形内,参考博客:http://www.cnblogs.com/graphics/archive/2010/08/05/1793393.html

我采用了第3种方法。

关键代码:

Dot* A4ShapeCorrect::morph(Triangle source, Triangle target, Dot p)
{
	Dot a = source.a, b = source.b, c = source.c;
	Dot at = target.a, bt = target.b, ct = target.c;

	if (p == a) return new Dot(at.x, at.y);
	if (p == b) return new Dot(bt.x, bt.y);
	if (p == c) return new Dot(ct.x, ct.y);

	Line cp(c, p);
	Line ab(a, b);
	Dot* d = cp.intersect(ab);
	// DA / BA
	double ABrate = (b.x - a.x != 0) ? (d->x - a.x) / (b.x - a.x) : (d->y - a.y) / (b.y - a.y);
	// PC / DC
	double CDrate = (d->x - c.x != 0) ? (p.x - c.x) / (d->x - c.x) : (p.y - c.y) / (d->y - c.y);

	double dtx = ABrate*(bt.x - at.x) + at.x;
	double dty = ABrate*(bt.y - at.y) + at.y;

	double ptx = CDrate*(dtx - ct.x) + ct.x;
	double pty = CDrate*(dty - ct.y) + ct.y;
	return new Dot(ptx, pty);
}

同样要注意方向:从目标像素映射到原像素,并且计算插值。

/* Method 2: Morphing */
Dot At(0, 0);
Dot Bt(target->width() - 1, 0);
Dot Ct(target->width() - 1, target->height() - 1);
Dot Dt(0, target->height() - 1);

Dot A(dots[0]->x, dots[0]->y);
Dot B(dots[1]->x, dots[1]->y);
Dot C(dots[2]->x, dots[2]->y);
Dot D(dots[3]->x, dots[3]->y);

cimg_forXY(*target, x, y)
{
	Dot P(x, y), *p = NULL;
	if (pointInTriangle(Triangle(At, Bt, Ct), P))
		p = morph(Triangle(At, Bt, Ct), Triangle(A, B, C), P);
	else if (pointInTriangle(Triangle(At, Ct, Dt), P))
		p = morph(Triangle(At, Ct, Dt), Triangle(A, C, D), P);
	if (p != NULL)
	{
		cimg_forC(*target, c)
			(*target)(x, y, 0, c) = bilinear(img, p->x, p->y, 0, c);
	}
}

对于以上两种方法,都涉及一个前提:我们找到的原图A4纸角点必须按顺序ABCD排列,或者我们至少知道各角点对应的A4纸方位。

我的方法是4个点先按y轴排序,y最小的不是A点就是B点,然后找跟这点最近的点,找到后这两个点就分别是A和B(事实上并不一定,比如扁菱形)。接着判断矩形是横的还是竖的,然后准确判断AB位置,最后准确判断CD位置。只能说So far so good!

void A4ShapeCorrect::reorderDots(std::vector<Dot*>& dots)
{
	std::sort(dots.begin(), dots.end(), [](const Dot* a, const Dot* b) { return a->y < b->y; });
	double min = DBL_MAX;
	int temp;
	for (int i = 1; i < dots.size(); ++i)
	{
		double dis = dots[0]->distance(*dots[i]);
		if (min > dis)
		{
			min = dis;
			temp = i;
		}
	}
	std::swap(dots[1], dots[temp]);
	if (dots[1]->y > dots[2]->y || dots[1]->y > dots[3]->y)
	{
		std::swap(dots[0], dots[1]);
		if (dots[2]->y > dots[3]->y)
		{
			std::swap(dots[2], dots[3]);
		}
	}
	else
	{
		if (dots[0]->x > dots[1]->x)
		{
			std::swap(dots[0], dots[1]);
		}
		if (dots[2]->x < dots[3]->x)
		{
			std::swap(dots[2], dots[3]);
		}
	}
}
时间: 2024-11-12 19:52:47

计算机视觉与模式识别(2)—— A4纸矫正的相关文章

一张A4纸最多可以折多少次

一张A4纸最多可以折多少次.先给出答案,基本上可以认为是4次.为什么可以这样得到结果呢.推理如下. A4纸是有严格的尺寸的规格的.210 × 297.单位为mm.先不严格讨论.假如长认为是26cm.人们可以接受的长度为2cm(就是肉眼可以接受的长度.可以自行设定).那么2^5(意思是2的五次方)的值为32.可见,2cm x 32倍 = 64cm.而我们纸的长度只有26cm.所以,折5次是不够的.答案基本上就是4次.注意:以上只讨论在一个方向上进行折叠的情况.如果考虑在两个方向上都可以折叠,那么结

网页打印A4纸-----表格在跨页时自动换页打印的实现 (转)

在最近所做的一个项目中,需要通过网页来打印不少的表单,但是又不想每个打印页签各占用一个页面,这样就需要生存很多不同的冗余页面,为了减少冗余,所有的表单通过jquery的页签tab来实现的. 一 :基本打印的实现: 1:tab页签在切换时的change事件中,记住每个页签的index, 2:在点击打印按钮时,根据所记住的index,由页签的id("tab名+index"组成),获取当前tab页签下所有的html; 3: 将获取到的需要打印页签的html赋值给document.body.i

经典的机器学习方面源代码库(非常全,数据挖掘,计算机视觉,模式识别,信息检索相关领域都适用的了)

经典的机器学习方面源代码库(非常全,数据挖掘,计算机视觉,模式识别,信息检索相关领域都适用的了) 今天给大家介绍一下经典的开源机器学习软件: 编 程语言:搞实验个人认为当然matlab最灵活了(但是正版很贵),但是更为前途的是python(numpy+scipy+matplotlib)和 C/C++,这样组合既可搞研究,也可搞商业开发,易用性不比matlab差,功能组合更为强大,个人认为,当然R和java也不错. 1.机器学习开源软件网(收录了各种机器学习的各种编程语言学术与商业的开源软件) h

A4纸网页打印 html网页页面的宽度设置成多少

A4纸竖向打印,html网页页面的宽度设置成多少?这个问题是我们大家所疑惑的,于是网上搜集整理下,希望可以帮助你们 最近开发项目时遇到了网页打印的问题,这是问题之二,打印宽度设置 在公制长度单位与屏幕分辨率进行换算时,必须用到一个DPI(Dot Per Inch)指标. 经过我仔细的测试,发现了网页打印中,默认采用的是96dpi,并非传闻的72dpi A4纸张的尺寸是210×297mm,按1英寸=25.41mm换算,即8.264×11.688英寸 所以,A4纸96dpi下的分辨率是794×112

制作A4纸打印的网页像素大小设置(转)

公司内做系统,要用A4纸打印东西,A4纸标准时mm,换算成像素不知道.网上找找,找到一篇文章,转一下,备用. A4纸的尺寸是210mm*297mm,也就是21.0cm*29.7cm,而1英寸=2.54cm,如果屏幕DPI分辨率为72像素/英寸,换算一下:相当于1cm可呈现 (72px/2.54cm) = 28.34px 下面是一些常用分辨率下A4纸在屏幕上的像素尺寸: 分辨率是72像素/英寸时,A4纸的尺寸的图像的像素是595×842: 分辨率是96像素/英寸时,A4纸的尺寸的图像的像素是794

计算机视觉与模式识别代码合集第二版two

Topic Name Reference code Image Segmentation Segmentation by Minimum Code Length AY Yang, J. Wright, S. Shankar Sastry, Y. Ma , Unsupervised Segmentation of Natural Images via Lossy Data Compression, CVIU, 2007 code Image Segmentation Normalized Cut

计算机视觉与模式识别代码合集第二版three

计算机视觉与模式识别代码合集第二版three     Topic Name Reference code Optical Flow Horn and Schunck's Optical Flow   code Optical Flow Black and Anandan's Optical Flow   code Pose Estimation Training Deformable Models for Localization Ramanan, D. "Learning to Parse I

PCB原比例打印到A4纸

最近在画一个四旋翼的PCB板子画完后想打印到A4纸上看看器件摆放的是否合理,因为我的电脑没有连接打印姐直接打印就没办法了,就直接用AD的智能PDF输出PDF格式的板子文件去打印,结果是打印出来的板子经过了放大失去了原来的比例.这样就失去了打印的意义了么.后来又发现一种比较方便了方法,在AD中  文件-页面设置-缩放比例-缩放模式选择Scale print 比例选择1:1-预览-打印-打印机选择名称为 Microsoft XPS Document Write ,这是Windows的一个文档管理软件

A1,A2,A3,A4纸的幅面规格

纸张幅面规格: 纸张的规格是指纸张制成后,经过修整切边,裁成一定的尺寸.过去是以多少"开"(例如8开或16开等)来表示纸张的大小,现在我采用国际标准,规定以 A0.A1.A2.B1.B2......等标记来表示纸张的幅面规格.标准规定纸张的幅宽(以X表示)和长度(以Y表示)的比例关系为X:Y=1: . 按 照纸张幅面的基本面积,把幅面规格分为A系列.B系列和C系列,幅面规格为A0的幅面尺寸为841mm×1189mm,幅面面积为1平方米:B0的幅面尺 寸为1000mm×1414mm,幅面