最近工厂需要检测出工价表面是否存在喷码是否断裂的情况,断裂的图片如图所示:
需要检测出断裂的字符以及断裂的位置,输出如下图:
实现思想:
1.分割出字符的外轮廓
2.计算每个外轮廓的连通域个数,多于两个的为断裂,设置i和j的阈值(正上方区域面积与其他连通域面积的比),但是还是有误检(如果其他字符也在正上方发生断裂,且面积比符合阈值),暂时还没有解决。
3.计算断裂的位置,连通域的最短距离的中点。
代码:
component.h
#ifndef __COMPONENT__
#define __COMPONENT__
#include <stdlib.h>
#define CALL_LabelComponent(x,y,returnLabel) { STACK[SP] = x; \
STACK[SP + 1] = y; STACK[SP + 2] = returnLabel; SP += 3; goto START; }
#define RETURN { SP -= 3; \
switch (STACK[SP + 2]) \
{ \
case 1: goto RETURN1; \
case 2: goto RETURN2; \
case 3: goto RETURN3; \
case 4: goto RETURN4; \
default: return; \
} \
}
#define X (STACK[SP-3])
#define Y (STACK[SP-2])
void LabelComponent(unsigned short* STACK, unsigned short width, unsigned short height,
unsigned char* input, int* output, int labelNo, unsigned short x, unsigned short y);
void LabelImage(unsigned short width, unsigned short height, unsigned char* input, int* output);
#endif
splitimg.h
#ifndef __SPLITIMG__
#define __SPLITIMG__
#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
typedef struct LINE_S
{
Point start;
Point end;
} LINE;
float pt_length(Point & pt1, Point & pt2);
// all input image must be black(0) frontground
bool get_slplit_line(Mat &src, vector<LINE> &line_arr);
bool get_split_box_big(Mat &src, vector<Rect> &rect_arr_big);
bool get_split_box_small(Mat &src, vector<Rect> &rect_arr_small);
bool compute_point(Mat &src, Mat &mask, Rect &rect, vector<Point> &point_arr);
#endif
component.cpp
#include "component.h"
void LabelComponent(unsigned short* STACK, unsigned short width, unsigned short height,
unsigned char* input, int* output, int labelNo, unsigned short x, unsigned short y)
{
STACK[0] = x;
STACK[1] = y;
STACK[2] = 0; /* return - component is labelled */
int SP = 3;
int index;
START: /* Recursive routine starts here */
index = X + width*Y;
if (input[index] == 0) RETURN; /* This pixel is not part of a component */
if (output[index] != 0) RETURN; /* This pixel has already been labelled */
output[index] = labelNo;
if (X > 0) CALL_LabelComponent(X - 1, Y, 1); /* left pixel */
RETURN1:
if (X < width - 1) CALL_LabelComponent(X + 1, Y, 2); /* right pixel */
RETURN2:
if (Y > 0) CALL_LabelComponent(X, Y - 1, 3); /* upper pixel */
RETURN3:
if (Y < height - 1) CALL_LabelComponent(X, Y + 1, 4); /* lower pixel */
RETURN4:
RETURN;
}
// input image must with black(0) backgroud and white frontground(1~255)
void LabelImage(unsigned short width, unsigned short height, unsigned char* input, int* output)
{
unsigned short* STACK = (unsigned short*)malloc(3 * sizeof(unsigned short)*(width*height + 1));
int labelNo = 0;
int index = -1;
for (unsigned short y = 0; y < height; y++)
{
for (unsigned short x = 0; x < width; x++)
{
index++;
if (input[index] == 0) continue; /* This pixel is not part of a component */
if (output[index] != 0) continue; /* This pixel has already been labelled */
/* New component found */
labelNo++;
LabelComponent(STACK, width, height, input, output, labelNo, x, y);
}
}
free(STACK);
}
splitimg.cpp
#include "splitimg.h"
float pt_length(Point & pt1, Point & pt2)
{
return sqrt(((float)pt1.x - (float)pt2.x)*((float)pt1.x - (float)pt2.x) + ((float)pt1.y - (float)pt2.y)*((float)pt1.y - (float)pt2.y));
}
/****************************************************************************************************
*********************************** usage of get_split_line ***********************************
/*vector<LINE> line_arr;
get_slplit_line(testImg, line_arr);
vector<LINE>::iterator iter = line_arr.begin();
for (; iter != line_arr.end(); iter++)
{
line(testImg, iter->start, iter->end, Scalar(0), 1, 8);
}
*****************************************************************************************************/
bool get_slplit_line(Mat &src, vector<LINE> &line_arr)
{
vector<int> coor;
for (int ii = 0; ii != src.rows; ii++)
{
int sum_black = 0;
for (int j = 0; j != src.cols; j++)
{
if (src.at<uchar>(ii, j) == 0)
sum_black++;
}
if (sum_black >= 1)
{
coor.push_back(ii);
}
}
if (coor.size() == 0)
return false;
vector<int> result;
vector<int>::iterator iter = coor.begin();
result.push_back(*iter);
int temp = *iter;
for (; iter != coor.end() - 1; iter++)
{
if (*(iter + 1) - temp == 1)
{
temp = *(iter + 1);
}
else
{
result.push_back(temp);
result.push_back(*(iter + 1));
temp = *(iter + 1);
}
}
result.push_back(*(coor.end() - 1));
vector<int>::iterator it = result.begin();
for (; it != result.end(); it++)
{
LINE line;
line.start = Point(0, *it);
line.end = Point(src.cols, *it);
line_arr.push_back(line);
}
return true;
}
bool get_split_box_big(Mat &src, vector<Rect> &rect_arr_big)
{
vector<LINE> line_arr;
if (!get_slplit_line(src, line_arr))
{
return false;
}
vector<LINE>::iterator iter = line_arr.begin();
for (; iter != line_arr.end(); iter+=2)
{
vector<int> col_line;
for (int ii = 0; ii != src.cols; ii++)
{
int sum_black = 0;
for (int j = iter->start.y; j != (iter + 1)->start.y; j++)
{
if (src.at<uchar>(j, ii) == 0)
sum_black++;
}
if (sum_black >= 1)
{
col_line.push_back(ii);
}
}
if (col_line.size() == 0)
return false;
vector<int> col_line_result;
vector<int>::iterator col_iter = col_line.begin();
col_line_result.push_back(*col_iter);
int temp = *col_iter;
for (; col_iter != col_line.end() - 1; col_iter++)
{
if (*(col_iter + 1) - temp == 1)
{
temp = *(col_iter + 1);
}
else
{
col_line_result.push_back(temp);
col_line_result.push_back(*(col_iter + 1));
temp = *(col_iter + 1);
}
}
col_line_result.push_back(*(col_line.end() - 1));
vector<int>::iterator col_line_result_it = col_line_result.begin();
for (; col_line_result_it != col_line_result.end(); col_line_result_it+=2)
{
int row_s = iter->start.y;
int row_e = (iter + 1)->start.y;
int col_s = *col_line_result_it;
int col_e = *(col_line_result_it + 1);
rect_arr_big.push_back(Rect(col_s, row_s, (col_e - col_s + 1), (row_e - row_s + 1)));
}
}
return true;
}
bool get_split_box_small(Mat &src, vector<Rect> &rect_arr_small)
{
vector<Rect> rect_arr;
if (!get_split_box_big(src, rect_arr))
{
return false;
}
vector<Rect>::iterator iter = rect_arr.begin();
for (; iter != rect_arr.end(); iter ++)
{
vector<int> row_line;
for (int ii = iter->y; ii < iter->y + iter->height; ii++)
{
int sum_black = 0;
for (int j = iter->x; j < iter->x + iter->width; j++)
{
if (src.at<uchar>(ii, j) == 0)
sum_black++;
}
if (sum_black >= 1)
{
row_line.push_back(ii);
}
}
if (row_line.size() == 0)
return false;
vector<int> row_line_result;
vector<int>::iterator row_iter = row_line.begin();
row_line_result.push_back(*row_iter);
int temp = *row_iter;
for (; row_iter != row_line.end() - 1; row_iter++)
{
if (*(row_iter + 1) - temp == 1)
{
temp = *(row_iter + 1);
}
else
{
row_line_result.push_back(temp);
row_line_result.push_back(*(row_iter + 1));
temp = *(row_iter + 1);
}
}
row_line_result.push_back(*(row_line.end() - 1));
vector<int>::iterator row_line_result_it = row_line_result.begin();
for (; row_line_result_it != row_line_result.end(); row_line_result_it += 2)
{
int row_s = *row_line_result_it;
int row_e = *(row_line_result_it + 1);
int col_s = iter->x;
int col_e = iter->x + iter->width - 1;
rect_arr_small.push_back(Rect(col_s, row_s, (col_e - col_s + 1), (row_e - row_s + 1)));
}
}
return true;
}
bool compute_point(Mat &src, Mat &mask, Rect &rect, vector<Point> &point_arr)
{
map<int, Point> p_begin;
map<int, Point> p_end;
map<int, Point >::iterator b_it;
map<int, Point >::iterator e_it;
map<int, vector<Point>> area;
map<int, vector<Point>>::iterator area_it;
vector<Point>::iterator point_it1;
vector<Point>::iterator point_it2;
for (int ii = rect.y; ii != (rect.y + rect.height); ii++)
{
for (int j = rect.x; j != (rect.x + rect.width); j++)
{
if (mask.at<int>(ii, j) != 0)
{
b_it = p_begin.find(mask.at<int>(ii, j));
if (b_it == p_begin.end())
{
p_begin[mask.at<int>(ii, j)] = Point(j, ii);
}
else
{
p_end[mask.at<int>(ii, j)] = Point(j, ii);
}
area[mask.at<int>(ii, j)].push_back(Point(j, ii));
}
}
}
if (area.size() > 1)
{
area_it = area.begin();
if ((float)area_it->second.size() / (float)(++area_it)->second.size() < 0.2 && area.size() == 2)
{
return false;
}
else
{
area_it = area.begin();
for (; area_it != --area.end(); area_it++)
{
vector<Point> tmp1 = area_it->second;
vector<Point> tmp2 = (++area_it)->second;
point_it1 = tmp1.begin();
float pt_len = 100000.;
Point st;
Point ed;
for (; point_it1 != tmp1.end(); point_it1++)
{
point_it2 = tmp2.begin();
for (; point_it2 != tmp2.end(); point_it2++)
{
if (pt_length(*point_it1, *point_it2) < pt_len)
{
pt_len = pt_length(*point_it1, *point_it2);
st = *point_it1;
ed = *point_it2;
}
}
}
int tmp_x = (st.x + ed.x) / 2;
int tmp_y = (st.y + ed.y) / 2;
point_arr.push_back(Point(tmp_x, tmp_y));
--area_it;
}
}
}
else
{
return false;
}
return true;
}
main.cpp
#include <opencv2/opencv.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <fstream>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <time.h>
#include "template.h"
#include "component.h"
#include "splitimg.h"
using namespace std;
using namespace cv;
int main(int argc, char **argv)
{
Mat testImg = imread("11.jpg");
clock_t start, finish;
double elapsed_time;
start = clock();
/*imshow("xx1", testImg);
waitKey(30);*/
Mat showImg = testImg.clone();
cvtColor(testImg, testImg, CV_BGR2GRAY);
threshold(testImg, testImg, 0, 255, CV_THRESH_OTSU);
Mat revert = ~testImg;
//start label Image
unsigned short W = revert.cols;
unsigned short H = revert.rows;
Mat connect_label = Mat::zeros(H, W, CV_32SC1);
LabelImage(W, H, (unsigned char*)revert.data, (int*)connect_label.data);
vector<Rect> rect_arr;
vector<Point> point_arr;
get_split_box_big(testImg, rect_arr);
vector<Rect>::iterator iter = rect_arr.begin();
for (; iter != rect_arr.end(); iter++)
{
rectangle(showImg, *iter, Scalar(0,255,0), 3, 8);
if (compute_point(testImg, connect_label, *iter, point_arr))
{
vector<Point>::iterator point_it = point_arr.begin();
for (; point_it != point_arr.end(); point_it++)
{
circle(showImg, *point_it, 10, Scalar(255, 0, 0), 3, 8);
}
}
}
finish = clock();
elapsed_time = finish - start;
printf("time:%f", elapsed_time);
imshow("xx", testImg);
waitKey(-1);
imshow("output", showImg);
waitKey(-1);
}