Защитил диплом, забыл приложить финальный код, может кому пригодится. Большое спасибо Smorodov и всем отписавшимся. Считаются все необходимые параметры(коэффициент смещения колеса, делительная окружность, модуль колеса). При необходимости реализации масштаба, нужно домножить все параметры на коэффициент масштаба. #include "opencv2/opencv.hpp"
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
int main(int ac, char** av)
{
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
vector<RotatedRect> minEllipse;
vector<RotatedRect> minRect;
string fname;
float alpha = 20;
if (ac > 1) {
fname = av[1];
alpha = ::atof(av[2]);
}
else {
cout << "Need filename!" << endl;
cin.get();
return 0;
}
Mat frame = imread(fname);
Mat drawing = frame.clone();
frame = 266 - frame;
namedWindow("result");
cvtColor(frame, frame, cv::COLOR_BGR2GRAY);
threshold(frame, frame, 1, 255, THRESH_BINARY);
Mat thr = frame.clone();
/// Find contours
findContours(frame, contours, hierarchy, cv::RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
vector<Point2f>center(contours.size());
vector<float>radius(contours.size());
vector<vector<Point> > contours_poly(contours.size());
for (int i = 0; i < contours.size(); i++)
{
approxPolyDP(Mat(contours[i]), contours_poly[i], 5, true);
minEnclosingCircle((Mat)contours_poly[i], center[i], radius[i]);
minRect.push_back(minAreaRect(Mat(contours[i])));
if (contours[i].size() > 5)
{
minEllipse.push_back(fitEllipse(Mat(contours[i])));
}
}
RNG rng(12345);
for (int i = 0; i< contours.size(); i++)
{
Scalar color = Scalar(255, 0, 0);
// contour
drawContours(drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point());
// ellipse
ellipse(drawing, minEllipse[i], color, 0, 8);
// rotated rectangle
Point2f rect_points[4]; minRect[i].points(rect_points);
for (int j = 0; j < 4; j++)
line(drawing, rect_points[j], rect_points[(j + 1) % 4], color, 1, 8);
// окружность
circle(drawing, center[i], cvRound(radius[i]), color, 1, 8, 0);
// ÷ентр окружности
line(drawing, cv::Point(center[i].x, 0), cv::Point(center[i].x, drawing.rows), Scalar(0, 0, 255));
line(drawing, cv::Point(0, center[i].y), cv::Point(drawing.cols, center[i].y), Scalar(0, 0, 255));
}
// засемплим точки с окружности.
float step = 0.001;
float x = center[0].x;
float y = center[0].y;
float r = radius[0] * 0.9;
Mat unrolled = Mat::zeros(100, CV_PI*2.0 / step, CV_8UC1);
int i = 0;
int tooth_thick = 0;
vector<int> teeth;
bool tooth = false;
float normals[2][2];
for (float ang = 0; ang<CV_PI * 2; ang += step)
{
float xs = x + r*cos(ang);
float ys = y + r*sin(ang);
uchar c = thr.at<uchar>(ys, xs);
drawing.at<Vec3b>(ys, xs) = Vec3b(0, 0, 255);
if (c>128)
{
if (teeth.size() == 1 && tooth == false) {
normals[0][0] = xs;
normals[0][1] = ys;
}
tooth = true;
tooth_thick++;
line(unrolled, cv::Point(i, 0), cv::Point(i, unrolled.rows), Scalar::all(255),5);
}
else {
if (tooth) {
teeth.push_back(tooth_thick);
tooth_thick = 0;
//count++;
if (teeth.size() == 1) {
normals[1][0] = xs;
normals[1][1] = ys;
}
}
tooth = false;
}
++i;
}
//do normals to surface of teeth
//line(drawing, Point(normals[0][0], normals[0][1]), Point(800 * cos(alpha * 180 / CV_PI), normals[0][1] * sin(alpha * 180 / CV_PI)), Scalar(0, 255, 0), 1);
//line(drawing, Point(normals[1][0], normals[1][1]), Point(800 * cos(alpha * 180 / CV_PI), normals[1][1] * sin(alpha * 180 / CV_PI)), Scalar(0, 255, 0), 1);
float m = minRect[0].size.width/(teeth.size()+2);//модуль
float d = m*teeth.size();//delitelnaya okrujnost
float Db = d*::cos(alpha);
float sb = (CV_PI*m) / 2; //tolshina po delitelnoy okrujnosti
alpha = alpha * CV_PI / 180;//for invol (convert deg to radian)
float X = ((normals[0][1] - normals[1][1]) - (Db*(sb / d + (tan(alpha)-alpha)))) / (2 * m*sin(alpha));
stringstream str;
str << "d= " + to_string(minRect[0].size.width) << " ";
str << "m= " + to_string(m) << " ";
str << "x= " + to_string(X) << " ";
//imshow("thr", thr);
//imshow("unrolled", unrolled);
Mat pic = cv::Mat::zeros(250, 250, CV_8UC3);
putText(pic,str.str(), cv::Point(5, 10), CV_FONT_NORMAL, 0.3, Scalar::all(255), 1, 7, false);
imshow("Результаты", pic);
imshow("result", drawing);
waitKey(0);
destroyAllWindows();
return 0;
}