您的位置:首页 > 其它

字符紧缩对齐

2015-11-08 19:12 337 查看
经过归一化处理的字符在图像图像中排列没有规律,这给后续的字符样本提取增加了工作量。因此,这里还要对字符进行缩紧和对齐。

代码如下:

/****************************************************************
功能:      对目标(如字符)进行缩紧和对齐,归一化之后使用
参数:      img:输入图像
dst:输出对齐后的图像
vector<RECT> vecRT:归一化之后所得的 字符分割的矩形框
注    :      只能处理二值图像
返回值:   返回分割字符的矩形框
***************************************************************/
vector<RECT> Ctry::ObjectAlign(IplImage* src, IplImage* dst, vector<RECT> vecRT)
{
int nNormW, nNormH;      //归一化之后统一矩形的宽、高
if (vecRT.size() > 0)
{
nNormW = vecRT[0].right - vecRT[0].left + 1;
nNormH = vecRT[0].bottom - vecRT[0].top + 1;
}

int nSrc_i, nSrc_j;   //映射源坐标
RECT  rt;
vector<RECT> rtNEW;
rtNEW.clear();
rtNEW.resize(vecRT.size());
int nMargin = 2;   //紧缩对齐后,图像留出左、上边界距离

//依次调整每个轮廓矩形区域的位置
for (int nObj = 0; nObj < vecRT.size(); nObj++)
{
rt = vecRT[nObj];
//紧缩对齐后的轮廓矩形
rtNEW[nObj].left = nObj*nNormW + nMargin;    //左边界
rtNEW[nObj].right = (nObj + 1)*nNormW + nMargin;
rtNEW[nObj].top = 0 + nMargin;    //上边界
rtNEW[nObj].bottom = nNormH + nMargin;

vecRT[nObj] = rtNEW[nObj];
//将原矩形框内的像素映射到新的矩形框中
for (int i = 0; i < nNormH; i++)
{
for (int j = nObj*nNormW; j < (nObj + 1)*nNormW; j++)
{
//计算映射源坐标
nSrc_i = rt.top + i;
nSrc_j = rt.left + j - nObj*nNormW;

double		pixel = cvGetReal2D(src, nSrc_i, nSrc_j);
cvSetReal2D(dst, i+nMargin, j+nMargin, pixel);
}
}
}
return  rtNEW;
}


完整的调用改程序的代码:(void Ctry::OnTryPath() //消息响应函数,函数从这里开始)


/****************************************************************
功能: 对前景目标(如字符)进行划分,将各个字符的轮廓矩形返回
参数: img:输入等待分割的图像
int areaThreshold:面积阈值,当分割后的字符小于这个值时,会擦除该字符,用于去除噪音等小区域杂点干扰
注 : 只能处理二值图像
返回值: 返回分割字符的矩形框
***************************************************************/
vector<RECT> Ctry::ObjectSegment(IplImage* img, int areaThreshold)
{
vector<RECT> vecRoughRECT; //粗略对象轮廓的矩形向量数组
vector<RECT> vecRECT; //精化后对象轮廓的矩形向量数组
vector<RECT> vecRECTBig; //存放精化对象区域中大的的矩形框
//清空用来表示每个对象的vector
vecRoughRECT.clear();
vecRECT.clear();
vecRECTBig.clear();

int nTop, nBttom; //整体前景区域的上下边界
int nObjCnt = 0; //对象数目

//从上向下扫描,找到整体区域的前景的上边界
for (int i = 0; i < img->height; i++)
{
for (int j = 0; j < img->width; j++)
{
double pixel = cvGetReal2D(img, i, j);
if (int(pixel) == 0)
{
nTop = i;
i = img->height; //对i赋大值,使得在break跳出内存循环后,直接在跳出外层循环
break;
}
}
}

//从下向上扫描,找到整体区域的前景的下边界
for (int i = img->height - 1; i >= 0; i--)
{
for (int j = 0; j < img->width; j++)
{
double pixel = cvGetReal2D(img, i, j);
if (int(pixel) == 0)
{
nBttom = i;
i = -1; //对i赋小值,使得在break跳出内存循环后,直接在跳出外层循环
break;
}
}
}

bool bStartSeg = false; //是否已经开始某一个对象的分割
bool bBlackInCol; //某一列中是否包含黑色像素

RECT rt;

//按列扫描,找到每一个目标的左右边界
for (int j = 0; j < img->width; j++)
{
bBlackInCol = false;
for (int i = 0; i < img->height; i++)
{
double pixel = cvGetReal2D(img, i, j);
if (int(pixel) == 0)
{
bBlackInCol = true; //该列中发现黑点
if (!bStartSeg) //还没有进入一个对象的分割
{
bStartSeg = true;//进入一个对象的分割
rt.left = j;
}
else
break;
}
}
if (j == (img->width - 1)) //扫描到最后一列了,说明整个图像扫描完毕
{
break;
}
//正处在分割状态,且扫描完一列都没有发现黑像素,表明当前对象分割完毕
if (bStartSeg && !bBlackInCol)
{
rt.right = j; //对象右边界确定

//对象的粗略上下边界(有待精化)
rt.top = nTop;
rt.bottom = nBttom;
::InflateRect(&rt, 1, 1); //矩形框膨胀一个像素,以免绘制时压到字符
vecRoughRECT.push_back(rt); //插入vector
bStartSeg = false; //当前分割结束
nObjCnt++; //对象数目加1
}
}

RECT rtNEW; //存放精化对象区域的矩形框
//由于得到了精确的左右边界,现在可以精化矩形框的上下边界
int nSize = vecRoughRECT.size();
for (int nObj = 0; nObj < nSize; nObj++)
{
rt = vecRoughRECT[nObj];
rtNEW.left = rt.left - 1;
rtNEW.right = rt.right + 1;
//从上向下逐行扫描边界
for (int i = rt.top; i < rt.bottom; i++)
{
for (int j = rt.left; j < rt.right; j++)
{
double pixel = cvGetReal2D(img, i, j);
if (int(pixel) == 0)
{
rtNEW.top = i - 1;
//对i赋大值,使得在break跳出内存循环后,直接在跳出外层循环
i = rt.bottom;
break;
}
}
}

//从下向上逐行扫描边界
for (int i = rt.bottom - 1; i > rt.top; i--)
{
for (int j = rt.left; j < rt.right; j++)
{
double pixel = cvGetReal2D(img, i, j);
if (int(pixel) == 0)
{
rtNEW.bottom = i + 1;
//对i赋小值,使得在break跳出内存循环后,直接在跳出外层循环
i = rt.top - 1;
break;
}
}
}
vecRECT.push_back(rtNEW);
}

//提取较大的框,擦除小的矩形框
for (int i = 0; i < vecRECT.size(); i++)
{
int x = vecRECT[i].left;
int y = vecRECT[i].top;
int x1 = vecRECT[i].right;
int y1 = vecRECT[i].bottom;
int area = (x1 - x)*(y1 - y); //矩形的面积
//当面积小于一个阈值时,擦除当前矩形框中的前景,否则将当前矩形框压入新的vector
if (area < areaThreshold)
{
for (int i = y; i < y1 + 1; i++)
{
for (int j = x; j < x1 + 1; j++)
{
cvSetReal2D(img, i, j, 255);
}
}
}
else
{
vecRECTBig.push_back(vecRECT[i]);
}
}

//画矩形框,显示分割字符
for (int i = 0; i < vecRECTBig.size(); i++)
{
int x = vecRECTBig[i].left - 1;
int y = vecRECTBig[i].top - 1;
int x1 = vecRECTBig[i].right + 1;
int y1 = vecRECTBig[i].bottom + 1;
CvPoint pt1(x, y);
CvPoint pt2(x1, y1);
//cvRectangle(img, pt1, pt2, CV_RGB(255, 0, 0), 1);
}
cvSaveImage("C:\\Users\\Administrator\\Desktop\\rect0.jpg", img);
return vecRECTBig;
}

void Ctry::OnTryPath() //消息响应函数,函数从这里开始
{
// TODO: 在此添加命令处理程序代码
IplImage* img = cvLoadImage("C:\\Users\\Administrator\\Desktop\\dst.jpg", -1);

vector<RECT> vecRECTBig, vecNew;
vecRECTBig = ObjectSegment(img, 260);

IplImage *norm = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
IplImage *dst1 = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
for (int i = 0; i < norm->height; i++)
{
for (int j = 0; j < norm->width; j++)
{
cvSetReal2D(norm, i, j, 255);
cvSetReal2D(dst1, i, j, 255);
}
}

for (int i = 0; i < vecRECTBig.size(); i++)
{
RECT rt = vecRECTBig[i]; //取得一个矩形轮廓
vecRECTBig[i] = RgnZoom(img, norm, 60, 100, rt); //将norm图对应的点设为黑色,返回缩放后的矩形框
}

vecNew = ObjectAlign(norm, dst1, vecRECTBig);

for (int i = 0; i < vecRECTBig.size(); i++)
{
int x = vecNew[i].left - 1;
int y = vecNew[i].top - 1;
int x1 = vecNew[i].right + 1;
int y1 = vecNew[i].bottom + 1;
CvPoint pt1(x, y);
CvPoint pt2(x1, y1);
cvRectangle(dst1, pt1, pt2, CV_RGB(255, 0, 0), 1);
}
cvSaveImage("C:\\Users\\Administrator\\Desktop\\tar.jpg", dst1);
}

/****************************************************************
功能: 字符归一化
参数: img:有待归一化的图像
dst:归一化后目标图像
int nTargWidth:归一化的目标宽度
int nTargHeight:归一化的目标高度
RECT lpRect:等待缩放的矩形框
注 : 只能处理二值图像
返回值: 缩放后的矩形框
***************************************************************/
RECT Ctry::RgnZoom(IplImage* img, IplImage* dst, int nTargWidth, int nTargHeight, RECT lpRect)
{
RECT retRT; //缩放后的区域矩形
double dXScale; //水平方向缩放因子
double dYScale; //竖直方法缩放因子

//确定缩放系数
dXScale = (double)nTargWidth / (lpRect.right - lpRect.left + 1);
dYScale = (double)nTargHeight / (lpRect.bottom - lpRect.top + 1);

int nSrc_i, nSrc_j; //映射源坐标

retRT.top = lpRect.top;
retRT.bottom = retRT.top + nTargHeight;
retRT.left = lpRect.left;
retRT.right = retRT.left + nTargWidth;

//对图像的区域矩形进行逐行扫描,通过像素映射完成缩放
for (int i = retRT.top; i < retRT.bottom; i++)
{
for (int j = retRT.left; j < retRT.right; j++)
{
//计算映射的源坐标(最邻近插值)
nSrc_i = retRT.top + int((i - retRT.top) / dYScale);
nSrc_j = retRT.left + int((j - retRT.left) / dXScale);

//对应像素赋值
double pixel = cvGetReal2D(img, nSrc_i, nSrc_j);
cvSetReal2D(dst, i, j, pixel);
}
}
return retRT;
}

/**************************************************************** 功能: 对目标(如字符)进行缩紧和对齐,归一化之后使用 参数: img:输入图像 dst:输出对齐后的图像 vector<RECT> vecRT:归一化之后所得的 字符分割的矩形框 注 : 只能处理二值图像 返回值: 返回分割字符的矩形框 ***************************************************************/ vector<RECT> Ctry::ObjectAlign(IplImage* src, IplImage* dst, vector<RECT> vecRT) { int nNormW, nNormH; //归一化之后统一矩形的宽、高 if (vecRT.size() > 0) { nNormW = vecRT[0].right - vecRT[0].left + 1; nNormH = vecRT[0].bottom - vecRT[0].top + 1; } int nSrc_i, nSrc_j; //映射源坐标 RECT rt; vector<RECT> rtNEW; rtNEW.clear(); rtNEW.resize(vecRT.size()); int nMargin = 2; //紧缩对齐后,图像留出左、上边界距离 //依次调整每个轮廓矩形区域的位置 for (int nObj = 0; nObj < vecRT.size(); nObj++) { rt = vecRT[nObj]; //紧缩对齐后的轮廓矩形 rtNEW[nObj].left = nObj*nNormW + nMargin; //左边界 rtNEW[nObj].right = (nObj + 1)*nNormW + nMargin; rtNEW[nObj].top = 0 + nMargin; //上边界 rtNEW[nObj].bottom = nNormH + nMargin; vecRT[nObj] = rtNEW[nObj]; //将原矩形框内的像素映射到新的矩形框中 for (int i = 0; i < nNormH; i++) { for (int j = nObj*nNormW; j < (nObj + 1)*nNormW; j++) { //计算映射源坐标 nSrc_i = rt.top + i; nSrc_j = rt.left + j - nObj*nNormW; double pixel = cvGetReal2D(src, nSrc_i, nSrc_j); cvSetReal2D(dst, i+nMargin, j+nMargin, pixel); } } } return rtNEW; }


原图:

效果图:(归一化效果图)


紧缩对齐效果图:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: