Sal*_*rus 5 c++ ocr scaling image-comparison bilinear-interpolation
*注意:尽管这篇文章是关于双线性插值的,但我还是保持标题的一般性,并提供了更多信息,以防有人对如何更好地做到这一点有所了解
为了创建单词搜索解决程序,我一直难以实现从图像中识别字母的方法。出于主要出于教育目的,也出于便携性的目的,我一直在尝试使用不使用库的方法。可以假设,将要拾取字符的图像除了拼图之外没有任何其他内容。尽管此页面只能识别少量字符,但我一直在用它来指导我的工作,这一个也一样 正如文章所建议的,我将每个字母的图像缩小为5x5,以将每个未知字母与之进行比较。通过使用双线性重采样将未知数缩小到5x5,并对已知和未知图像中每个相应像素的强度差的平方求和,我获得了最大的成功。为了尝试获得更准确的结果,我还添加了每个图像的上半部分和下半部分的宽度:高度比率和白色:黑色像素比率之差的平方。然后,将具有与未知图像最接近的“差异分数”的已知图像视为未知字母。问题在于,这似乎只有大约50%的准确性。为了改善这一点,我尝试使用较大的样本(而不是5x5,我尝试使用15x15),但是事实证明效果不佳。我还尝试浏览已知和未知的图像,寻找特征和形状,然后基于具有相同数量的相同特征的两个图像确定匹配项。例如,识别并计数了以下形状:?代表黑色像素)。事实证明,这种方法不如原始方法有效。
? ? ? ?
? ?
Run Code Online (Sandbox Code Playgroud)
因此,这里是一个示例:加载以下图像:
然后,该程序通过使用面积总和表确定每个像素的强度是否高于或低于11x11平方的平均强度,将其转换为单色,修复倾斜并通过识别相对相等间距的区域来挑选字母。然后,我使用相交的水平和垂直空间来大致了解每个字符的位置。接下来,我要确保在每个方块中都包含整个字母,方法是在原始方块的上方,下方,左侧和右侧逐行查找,直到该方块的边界未检测到深色像素为止。
然后,我将每个字母都重新采样,然后将其与已知图像进行比较。
*注意:已知样本使用的字体大小为12,使用双线性插值法在photoshop中重新缩放为5x5。
这是一个成功匹配的示例:选出以下字母:
缩小为:
看起来像
远道而来。这已成功匹配已知的N个样本:
这是失败的比赛:
被挑选出来并缩小为:
毫不奇怪,它与已知的R样本不匹配
我更改了图像的拾取方式,以致如上图所示,该字母不会被截断,因此我认为问题出在缩小图像上。目前,我正在使用双线性插值对图像进行重新采样。要了解究竟是如何工作原理与采样我提到的第二个答案的这个职位,并用下面的代码上来。以前,我已经测试过此代码可以正常工作(至少达到“看起来不错”的程度),因此它可能是引起问题的多种因素的组合。
void Image::scaleTo(int width, int height)
{
int originalWidth = this->width;
int originalHeight = this->height;
Image * originalData = new Image(this->width, this->height, 0, 0);
for (int i = 0; i < this->width * this->height; i++) {
int x = i % this->width;
int y = i / this->width;
originalData->setPixel(x, y, this->getPixel(x, y));
}
this->resize(width, height); //simply resizes the image, after the resize it is just a black bmp.
double factorX = (double)originalWidth / width;
double factorY = (double)originalHeight / height;
float * xCenters = new float[originalWidth]; //the following stores the "centers" of each pixel.
float * yCenters = new float[originalHeight];
float * newXCenters = new float[width];
float * newYCenters = new float[height];
//1 represents one of the originally sized pixel's side length
for (int i = 0; i < originalWidth; i++)
xCenters[i] = i + 0.5;
for (int i = 0; i < width; i++)
newXCenters[i] = (factorX * i) + (factorX / 2.0);
for (int i = 0; i < height; i++)
newYCenters[i] = (factorY * i) + (factorY / 2.0);
for (int i = 0; i < originalHeight; i++)
yCenters[i] = i + 0.5;
/* p[0] p[1]
p
p[2] p[3] */
//the following will find the closest points to the sampled pixel that still remain in this order
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
POINT p[4]; //POINT used is the Win32 struct POINT
float pDists[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
float xDists[4];
float yDists[4];
for (int i = 0; i < originalWidth; i++) {
for (int j = 0; j < originalHeight; j++) {
float xDist = abs(xCenters[i] - newXCenters[x]);
float yDist = abs(yCenters[j] - newYCenters[y]);
float dist = sqrt(xDist * xDist + yDist * yDist);
if (xCenters[i] < newXCenters[x] && yCenters[j] < newYCenters[y] && dist < pDists[0]) {
p[0] = { i, j };
pDists[0] = dist;
xDists[0] = xDist;
yDists[0] = yDist;
}
else if (xCenters[i] > newXCenters[x] && yCenters[j] < newYCenters[y] && dist < pDists[1]) {
p[1] = { i, j };
pDists[1] = dist;
xDists[1] = xDist;
yDists[1] = yDist;
}
else if (xCenters[i] < newXCenters[x] && yCenters[j] > newYCenters[y] && dist < pDists[2]) {
p[2] = { i, j };
pDists[2] = dist;
xDists[2] = xDist;
yDists[2] = yDist;
}
else if (xCenters[i] > newXCenters[x] && yCenters[j] > newYCenters[y] && dist < pDists[3]) {
p[3] = { i, j };
pDists[3] = dist;
xDists[3] = xDist;
yDists[3] = yDist;
}
}
}
//channel is a typedef for unsigned char
//getOPixel(point) is a macro for originalData->getPixel(point.x, point.y)
float r1 = (xDists[3] / (xDists[2] + xDists[3])) * getOPixel(p[2]).r + (xDists[2] / (xDists[2] + xDists[3])) * getOPixel(p[3]).r;
float r2 = (xDists[1] / (xDists[0] + xDists[1])) * getOPixel(p[0]).r + (xDists[0] / (xDists[0] + xDists[1])) * getOPixel(p[1]).r;
float interpolated = (yDists[0] / (yDists[0] + yDists[3])) * r1 + (yDists[3] / (yDists[0] + yDists[3])) * r2;
channel r = (channel)round(interpolated);
r1 = (xDists[3] / (xDists[2] + xDists[3])) * getOPixel(p[2]).g + (xDists[2] / (xDists[2] + xDists[3])) * getOPixel(p[3]).g; //yDist[3]
r2 = (xDists[1] / (xDists[0] + xDists[1])) * getOPixel(p[0]).g + (xDists[0] / (xDists[0] + xDists[1])) * getOPixel(p[1]).g; //yDist[0]
interpolated = (yDists[0] / (yDists[0] + yDists[3])) * r1 + (yDists[3] / (yDists[0] + yDists[3])) * r2;
channel g = (channel)round(interpolated);
r1 = (xDists[3] / (xDists[2] + xDists[3])) * getOPixel(p[2]).b + (xDists[2] / (xDists[2] + xDists[3])) * getOPixel(p[3]).b; //yDist[3]
r2 = (xDists[1] / (xDists[0] + xDists[1])) * getOPixel(p[0]).b + (xDists[0] / (xDists[0] + xDists[1])) * getOPixel(p[1]).b; //yDist[0]
interpolated = (yDists[0] / (yDists[0] + yDists[3])) * r1 + (yDists[3] / (yDists[0] + yDists[3])) * r2;
channel b = (channel)round(interpolated);
this->setPixel(x, y, { r, g, b });
}
}
delete[] xCenters;
delete[] yCenters;
delete[] newXCenters;
delete[] newYCenters;
delete originalData;
}
Run Code Online (Sandbox Code Playgroud)
我对任何愿意远程筛选并尝试帮助的人表示最大的敬意。任何和所有建议将不胜感激。
更新: 因此,正如我建议的那样,我开始使用单词搜索中按比例缩小的字母来扩充已知数据集。这将准确度从大约50%大大提高到70%(根据很小的样本量计算得出的百分比,因此请轻拿轻放)。基本上,我使用原始字符集作为基础(原始字符集实际上是我尝试过的其他字符集中最准确的:使用相同重采样算法计算出的字符集,使用不同字体的集合等)我只是手动将已知信息添加到该集合中。我基本上将手动分配在搜索中挑选出的前20张左右的图像及其对应的字母,并将其保存到已知的set文件夹中。我仍在从整个已知集合中选择最接近的一个来匹配一个字母。这仍然是一个好方法还是应该进行某种更改?我还实现了一项功能,如果一个字母与一个已知字母的匹配度大约为90%,则我认为匹配是正确的,并且当前的“未知”与已知列表匹配。我可以看到这可能是双向的,我觉得可以。使程序随着时间的推移更加准确或b。巩固最初的猜测,并可能使程序随着时间的推移准确性降低。我实际上没有注意到这会导致变化(无论好坏)。我是否在正确的轨道上?我不会称之为解决问题,直到我获得更高的准确性并从更多示例中测试该程序。到已知清单。我可以看到这可能是双向的,我觉得可以。使程序随着时间的推移更加准确或b。巩固最初的猜测,并可能使程序随着时间的推移准确性降低。我实际上没有注意到这会导致变化(无论好坏)。我是否在正确的轨道上?我不会称之为解决问题,直到我获得更高的准确性并从更多示例中测试该程序。到已知清单。我可以看到这可能是双向的,我觉得可以。使程序随着时间的推移更加准确或b。巩固最初的猜测,并可能使程序随着时间的推移准确性降低。我实际上没有注意到这会导致变化(无论好坏)。我是否在正确的轨道上?我不会称之为解决问题,直到我获得更高的准确性并从更多示例中测试该程序。
| 归档时间: |
|
| 查看次数: |
1119 次 |
| 最近记录: |