#ifndef YN_H
#define YN_H
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
namespace jyn
{
int maxcnt(vector<double> v, double a);
int max(vector<double> v);
double avg(vector<double> v);
vector<double> divide_x(Mat img);
vector<double> divide_y(Mat img);
bool roundCV(Mat img, int start, int end, int type);
int outline_cnt(Mat img);
Mat CenterOfGravity(Mat img, Mat centroids, int direction, int type);
Mat label(Mat img);
int pix_val(Mat img);
bool divide(Mat img, Mat centroids);
bool plus_d(Mat img);
bool mul(Mat img, Mat centroids);
bool parentheses(Mat img, Mat centroids, int type);
extern String message;
void val(Mat img, Mat stats);
void message_diviison(Mat img, vector<vector<Point>> contours, Mat centroids, Mat stats, Mat firstimg);
void Object_Recognition(Mat img);
void onMouse(int event, int x, int y, int flags, void* userdata);
}
#endif
#include "yn.h"
using namespace jyn;
int main(void) {
Mat img(500, 1700, CV_8UC1, Scalar(0));
namedWindow("img");
setMouseCallback("img", onMouse, &img);
imshow("img", img);
waitKey();
}
#include "yn.h"
namespace jyn {
int maxcnt(vector<double> v, double a) {// a보다 큰 값을 가진 벡터 개수
int cnt = 0;
for (int i = 0; i < 10; i++)
{
if (v[i] > a) {
cnt++;
}
}
return cnt;
}
int max(vector<double> v) { //최대값 인덱스
int maxindex = 0;
for (int i = 0; i < 10; i++)
{
if (v[maxindex] < v[i]) {
maxindex = i;
}
}
return maxindex;
}
double avg(vector<double> v) { //벡터값의 평균
double avg = 0;
for (int i = 0; i < v.size(); i++)
{
avg += v[i];
}
avg = avg / v.size();
return avg;
}
vector<double> divide_x(Mat img) { //분석x
Mat dst(img.size(), img.depth(), Scalar(0));
vector<double> sum;
vector<double> avgx;
sum.resize(10);
avgx.resize(10);
for (int k = 0; k < 10; k++)
{
for (int i = 0; i <= img.cols; i++) //x축으로 1/10 나눈 부분의 평균 픽셀값 개수 저장 ||||
{
for (int j = 0; j < img.rows; j++)
{
if (i >= img.cols / 10 * k && i < img.cols / 10 * (k + 1)) {
if (img.at<uchar>(j, i) == 255) {
sum[k] += 1;
}
}
}
}
}
for (int i = 0; i < 10; i++)
{
avgx[i] = sum[i] / img.rows * (img.cols / 10);
// cout << i << "번 x축 평균개수:" << avgx[i] << endl; //x축1/10부분 픽셀값 255인 부분의 평균개수
}
return avgx;
}
vector<double> divide_y(Mat img) { //분석y
Mat dst(img.size(), img.depth(), Scalar(0));
vector<double> sum;
vector<double> avgy;
sum.resize(10);
avgy.resize(10);
for (int k = 0; k < 10; k++)
{
for (int i = 0; i <= img.rows; i++) //y축으로 1/10 나눈 부분의 평균 픽셀값 개수 저장 =
{
for (int j = 0; j < img.cols; j++)
{
if (i >= img.rows / 10 * k && i < img.rows / 10 * (k + 1)) {
if (img.at<uchar>(i, j) == 255) {
sum[k] += 1;
}
}
}
}
}
for (int i = 0; i < 10; i++)
{
avgy[i] = sum[i] / img.cols * (img.rows / 10);
// cout << i << "번 y축 평균개수:" << avgy[i] << endl; //y축1/10부분 픽셀값 255인 부분의 평균개수
}
return avgy;
}
bool roundCV(Mat img, int start, int end, int type) {//값을 반올림해서 같은지 판별하는 함수 , type==1==avgx , 2==avgy
vector<double> avgx = divide_x(img);
vector<double> avgy = divide_y(img);
int ox = 1;
for (int i = start; i < end; i++)
{
if (type == 1) {
if (cvRound(avgx[i]) == cvRound(avgx[i + 1])) {
continue;
}
else {
return false;
}
}
else {
if (cvRound(avgy[i]) == cvRound(avgy[i + 1])) {
continue;
}
else {
return false;
}
}
}
return true;
}
int outline_cnt(Mat img) {
vector<vector<Point>> contours;
findContours(img, contours, RETR_LIST, CHAIN_APPROX_NONE);
//cout << contours.size() << "개 : 부분 행렬의 외각선 개수" << endl;
return contours.size();
}
Mat CenterOfGravity(Mat img, Mat centroids, int direction, int type) { //무게중심으로 객체 자르기 x방향:1_ , y방향:2 | , 양방향+: 3(type은 왼쪽 위부터 오른쪽으로 1~4)
Mat dst(img.size(), img.depth(), Scalar(0));
double x = centroids.at<double>(1, 0);
double y = centroids.at<double>(1, 1);
if (direction == 1) {
if (type == 1) {
dst = img(Rect(Point(0, 0), Size(img.cols, y)));
}
else {
dst = img(Rect(Point(0, y), Size(img.cols, img.rows - y)));
}
}
else if (direction == 2) {
if (type == 1) {
dst = img(Rect(Point(0, 0), Size(x, img.rows)));
}
else {
dst = img(Rect(Point(x, 0), Size(img.cols - x, img.rows)));
}
}
else if (direction == 3) {
if (type == 1) {
dst = img(Rect(0, 0, x, y));
}
else if (type == 2) {
dst = img(Rect(x, 0, img.cols - x, y));
}
else if (type == 3) {
dst = img(Rect(0, y, x, img.rows - y));
}
else {
dst = img(Rect(x, y, img.cols - x, img.rows - y));
}
}
return dst;
}
Mat label(Mat img) {//부분 객체 레이블링, 무게중심 반환
Mat labels, stats, centroids;
Mat dst;
img.copyTo(dst);
int cnt = connectedComponentsWithStats(dst, labels, stats, centroids);//레이블링
return centroids;
}
int pix_val(Mat img) { //픽셀값 개수 세기
int pix = 0;
for (int i = 0; i < img.rows; i++)
{
for (int j = 0; j < img.cols; j++)
{
if (img.at<uchar>(i, j) > 0) {
pix++;
}
}
}
return pix++;
}
bool divide(Mat img, Mat centroids) {
if (pix_val(CenterOfGravity(img, centroids, 3, 1)) * 5 < pix_val(CenterOfGravity(img, centroids, 3, 2))) {
return true;
}
else {
return false;
}
}
bool plus_d(Mat img) { //이 박스안에 객체있으면 false
vector<Point> p;
p.push_back(Point(0, 0));
p.push_back(Point(img.cols / 4 * 3, 0));
p.push_back(Point(0, img.rows / 4 * 3));
p.push_back(Point(img.cols / 4 * 3, img.rows / 4 * 3));
for (int i = 0; i < p.size(); i++)
{
Mat m = img(Rect(p[i], Size(img.cols / 4, img.rows / 4)));
for (int k = 0; k < m.rows; k++)
{
for (int j = 0; j < m.cols; j++)
{
if (m.at<uchar>(k, j) > 0) {
return false;
}
}
}
//rectangle(img, Rect(p[i], Size(img.cols / 4, img.rows / 4)), Scalar(200), 1);
}
return true;
}
bool mul(Mat img, Mat centroids) { //무게중심 위아래양옆의 끝에 객체 있으면 false
double x = centroids.at<double>(1, 0);
double y = centroids.at<double>(1, 1);
vector<Point> p;
p.push_back(Point(x - (img.cols / 7), 0));
p.push_back(Point(x - (img.cols / 7), img.rows / 7 * 6));
p.push_back(Point(0, y - (img.rows / 7)));
p.push_back(Point(img.cols / 7 * 6, y - (img.rows / 7)));
for (int i = 0; i < p.size(); i++)
{
Mat m = img(Rect(p[i], Size(img.cols / 7, img.rows / 7)));
for (int k = 0; k < m.rows; k++)
{
for (int j = 0; j < m.cols; j++)
{
if (m.at<uchar>(k, j) > 0) {
return false;
}
}
}
//rectangle(img, Rect(p[i], Size(img.cols / 7, img.rows / 7)), Scalar(200), 1); //확인
//imshow("mul", img);
//waitKey();
}
return true;
}
bool parentheses(Mat img, Mat centroids, int type) {//type==1==왼쪽 괄호, type==2==오른쪽
double x = centroids.at<double>(1, 0);
double y = centroids.at<double>(1, 1);
if (type == 1) {
Mat m = img(Rect(Point(x, y - img.rows / 7), Size(img.cols - x, img.rows / 3)));
for (int k = 0; k < m.rows; k++)
{
for (int j = 0; j < m.cols; j++)
{
if (m.at<uchar>(k, j) > 0) {
return false;
}
}
}
//rectangle(img, Rect(Point(x + img.cols / 4, y - img.rows / 7), Size(img.cols - x, img.rows / 3)), Scalar(200), 1); //확인
//imshow("mul", img);
//waitKey();
}
else {
Mat m = img(Rect(Point(0, y - img.rows / 7), Size(x, img.rows / 3)));
for (int k = 0; k < m.rows; k++)
{
for (int j = 0; j < m.cols; j++)
{
if (m.at<uchar>(k, j) > 0) {
return false;
}
}
}
//rectangle(img, Rect(Point(0, y - img.rows / 7), Size(x, img.rows / 3)), Scalar(200), 1); //확인
//imshow("mul", img);
//waitKey();
}
return true;
//circle(img, Point(x, y), 3, Scalar(200), -1);
//imshow("center", img);
//waitKey();
}
String message = "";
void val(Mat img, Mat stats) {
int* p = stats.ptr<int>(stats.rows - 1);
putText(img, message, Point(p[0] + p[2] + 50, p[1]), FONT_HERSHEY_SIMPLEX, 3.0, Scalar(255), 3);
imshow("img", img);
}
void message_diviison(Mat img, vector<vector<Point>> contours, Mat centroids, Mat stats, Mat firstimg) { // 문자 구분
vector<double> avgx = divide_x(img);
vector<double> avgy = divide_y(img);
Mat gravity_X;
static int cntminus = 0, cntminus_p = 0;
static int cnt = 0;
static int pcnt;
static bool ox = false;
// gravity_X = CenterOfGravity(img, centroids, 1, 1);//x축 방향으로 객체의 무게중심 자르기 ,위쪽 부분
// Mat gravity_Xdown = CenterOfGravity(img, centroids, 1, 2);//x축 방향으로 객체의 무게중심 자르기 ,위쪽 부분
// Mat gravity_Y = CenterOfGravity(img, centroids, 2,1); //y축 방향으로 객체 무게중심 자르기
if (contours.size() == 1) {
if (avg(avgx) < 1) { //1은 x축으로 나눈 구간들의 평균이 1이하일때
message += "1";
cout << "1" << endl;
}
else if (avg(avgy) < 1) { //y축으로 나눈 구간들의 평균이 1이하일때 : 마이너스
message += "-";
cout << "-" << endl;
cntminus = cnt;
}
else if (divide(img, centroids)) {
message += "/";
cout << "/" << endl;
}
else if (plus_d(img)) {
message += "+";
cout << "+" << endl;
}
else if (mul(img, centroids)) {
message += "x";
cout << "x" << endl;
}
else if (parentheses(img, centroids, 1)) {
message += "(";
cout << "(" << endl;
}
else if (parentheses(img, centroids, 2)) {
message += ")";
cout << ")" << endl;
}
else if ((outline_cnt(CenterOfGravity(img, centroids, 2, 1))) == 2) { // 무게중심을 기준으로 x축에 수직인 직선으로 나눈 왼쪽 행렬의 외각선이 2개
if (max(avgy) == 9 || roundCV(img, 7, 9, 1)) {
message += "2";
cout << "2" << endl;
}
else {
message += "5";
cout << "5" << endl;
}
}
else if ((outline_cnt(CenterOfGravity(img, centroids, 2, 1))) == 3) {
message += "3";
cout << "3" << endl;
}
else if (roundCV(img, 5, 7, 1) || ((int)avgx[6] == (int)avgx[7] && (int)avgx[7] == (int)avgx[5] && (int)avgx[5] == (int)avgx[6]) || roundCV(img, 6, 8, 0)) {
message += "7";
cout << "7" << endl;
}
}
if (contours.size() == 2) {
if ((outline_cnt(CenterOfGravity(img, centroids, 1, 0))) == 2) { //y축 무게중심으로 객체 잘랐을때 아래쪽부분의 외각선 개수
if ((int)avgy[6] == (int)avgy[7] && (int)avgy[7] == (int)avgy[8] && (int)avgy[8] == (int)avgy[6] || roundCV(img, 6, 8, 0)) {
message += "9";
cout << "9" << endl;
}
else {
message += "6";
cout << "6" << endl;
}
}
else if (roundCV(img, 8, 9, 1) || (int)avgx[8] == (int)avgx[9]) {
message += "4";
cout << "4" << endl;
}
else if ((outline_cnt(CenterOfGravity(img, centroids, 1, 1))) == 2 || (int)avgy[6] == (int)avgy[7] && (int)avgy[7] == (int)avgy[8] && (int)avgy[8] == (int)avgy[6] || roundCV(img, 6, 8, 0)) {
message += "9";
cout << "9" << endl;
}
else if (((outline_cnt(CenterOfGravity(img, centroids, 1, 1))) == 1 && (outline_cnt(CenterOfGravity(img, centroids, 1, 0))) == 1) || (roundCV(img, 4, 6, 0) || ((int)avgy[6] == (int)avgy[5] && (int)avgy[5] == (int)avgy[4] && (int)avgy[4] == (int)avgy[6]))) {
message += "0";
cout << "0" << endl;
}
}
if (contours.size() == 3) {
message += "8";
cout << "8" << endl;
}
cnt++;
}
void Object_Recognition(Mat img) { //객체 인식
Mat labels, stats, centroids;
int cnt = connectedComponentsWithStats(img, labels, stats, centroids);//레이블링
//cout << stats.rows<<"개의 레이블 배경포함 객체수"<<endl;
vector<Rect> r;
for (int i = 0; i < cnt; i++) //바운딩 박스 정보 vector<Rect>r에 저장
{
int* p = stats.ptr<int>(i);
r.push_back(Rect(p[0], p[1], p[2], p[3]));
}
Rect tmp;
for (int i = 1; i < r.size(); i++) // 벡터 r의 저장된 순서를 x좌표가 작은순부터 저장되도록 정렬
{
for (int j = 0; j < r.size() - i; j++)
{
if (r[j].x > r[j + 1].x) {
tmp = r[j];
r[j] = r[j + 1];
r[j + 1] = tmp;
}
}
}
for (int i = 1; i < r.size(); i++)
{
//cout << r[i] << endl;
}
for (int i = 1; i < r.size(); i++) // 객체 외각선 검출
{
vector<vector<Point>> contours;
findContours(img(r[i]), contours, RETR_LIST, CHAIN_APPROX_NONE);
//cout << contours.size() << "개의 외각선" << endl;
Mat dst;
img(r[i]).copyTo(dst);
// divide_x(&dst);
message_diviison(dst, contours, label(dst), stats, img);
}
putText(img, "=" + message, Point(r[cnt - 1].x + r[cnt - 1].width + 50, r[cnt - 1].y + r[cnt - 1].height - 10), FONT_HERSHEY_SIMPLEX, 3.0, Scalar(255), 1);
imshow("img", img);
}
void onMouse(int event, int x, int y, int flags, void* userdata) {
Mat img = *(Mat*)userdata;
static Point ptOld;
if (event == EVENT_LBUTTONDOWN) {
ptOld = Point(x, y);
}
else if (event == EVENT_MOUSEMOVE) {
if (flags & EVENT_FLAG_LBUTTON) {
line(img, ptOld, Point(x, y), Scalar(255), 6);
imshow("img", img);
ptOld = Point(x, y);
}
}
else if (event == EVENT_RBUTTONUP) {
Object_Recognition(img); //객체 인식
}
}
}