一,预处理,对图像进行阀值处理,消除所有颜色信息。
Mat binarize(Mat input)
{
Mat binaryImage;
cvtColor(input, input, COLOR_BGR2GRAY);
threshold(input, binaryImage, 0, 255, THRESH_OTSU);
//Count the number of black and white pixels
int white = countNonZero(binaryImage);
int black = binaryImage.size().area() - white;
//If the image is mostly white (white background), invert it
return white < black ? binaryImage : ~binaryImage;
}
Otsu方法使类间方差最大化。
二,文本分割,
1,使用连通分量分析:搜索图像中连贯的像素组。
第一步创建连贯区域,使用扩张形态学算子,膨胀让图像元素更厚。
首先创建形态学3x3交叉内核,以上内核应用5次膨胀,将所有字母粘合在一起。
第二步识别段落块,执行连通组件分析查找与段落对应的块。
检索外部轮廓并使用简单近似。
第三步确定每个轮廓的最小旋转边界矩形。
矩形宽高小于20个像素丢弃,宽高比小于2,同时需要考虑旋转的边界框。
vector<RotatedRect> findTextAreas(Mat input) {
auto kernel = getStructuringElement(MORPH_CROSS, Size(3,3));
Mat dilated;
dilate(input, dilated, kernel, cv::Point(-1, -1), 5);
vector<vector<Point>> contours;
findContours(dilated, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
vector<RotatedRect> areas;
for (const auto& contour : contours)
{
auto box = minAreaRect(contour);
if (box.size.width < 20 || box.size.height < 20)
continue;
double proportion = box.angle < -45.0 ?
box.size.height / box.size.width :
box.size.width / box.size.height;
if (proportion < 2)
continue;
areas.push_back(box);
}
return areas;
}
2,使用分类器搜索以前训练过的字母纹理图案:对诸如Haralick特征等纹理特征,经常使用小波变换。下一篇介绍。
三,文本提取和偏斜调整
Mat deskewAndCrop(Mat input, const RotatedRect& box)
{
double angle = box.angle;
auto size = box.size;
//Adjust the box angle
if (angle < -45.0)
{
angle += 90.0;
std::swap(size.width, size.height);
}
//Rotate the text according to the angle
auto transform = getRotationMatrix2D(box.center, angle, 1.0);
Mat rotated;
warpAffine(input, rotated, transform, input.size(), INTER_CUBIC);
//Crop the result
Mat cropped;
getRectSubPix(rotated, size, box.center, cropped);
copyMakeBorder(cropped,cropped,10,10,10,10,BORDER_CONSTANT,Scalar(0));
return cropped;
}
角度小于-45度时文本垂直,旋转角度增加90度,切换宽度和高度。
//描述旋转的2D仿射变换矩阵
Mat getRotationMatrix2D( Point2f center, double angle, double scale );
//旋转自身
void warpAffine( InputArray src, OutputArray dst,
InputArray M, Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar());
flags表示图像应如何插值,BICUBIC_INTERPOLATION提高质量,默认值为LINEAR_INTERPOLATION。
borderMode边框模式。
Scalar边框颜色。
//裁剪边界框的矩形区域
void getRectSubPix( InputArray image, Size patchSize,
Point2f center, OutputArray patch, int patchType = -1 );
//图像周围添加边框
void copyMakeBorder(InputArray src, OutputArray dst,
int top, int bottom, int left, int right,
int borderType, const Scalar& value = Scalar() );
分类阶段需要在文本周围留出边缘。
整体调用
int main(int argc, char* argv[])
{
auto ticket = binarize(imread("ticket.png"));
auto regions = findTextAreas(ticket);
int count = 0;
for (const auto& region : regions) {
auto cropped = deskewAndCrop(ticket, region);
stringstream ss;
ss << "Cropped text " << count++;
imshow(ss.str(), cropped);
}
waitKey(0);
return 0;
}