Я хотел обнаружить эллипс в изображении. Так как в то время я изучал Mathematica, я задал вопрос Вот и получил удовлетворительный результат из ответа ниже, который использовал алгоритм RANSAC для обнаружения эллипса.
Однако в последнее время мне нужно перенести его на OpenCV, но есть некоторые функции, которые существуют только в Mathematica. Одной из ключевых функций является функция «GradientOrientationFilter».
Поскольку для общего эллипса существует пять параметров, мне нужно выбрать пять точек, чтобы определить один. Тем не менее, чем больше точек выборки, тем ниже вероятность получить правильное предположение, что приводит к снижению вероятности обнаружения эллипса. Поэтому в ответе Mathematica добавится еще одно условие, то есть градиент изображения должен быть параллельным градиенту уравнения эллипса. В любом случае, нам потребуется всего три точки, чтобы определить один эллипс, используя метод наименьших квадратов из подхода Mathematica. Результат довольно хороший.
Однако, когда я пытаюсь найти градиент изображения с помощью оператора Собеля или Шарра в OpenCV, это не достаточно хорошо, что всегда приводит к плохому результату.
Как точно рассчитать градиент или тангенс изображения? Спасибо!
Результат с градиентом, три очка
Результат без градиента, пять баллов
———-обновлено ———-
Я заранее определил края и срединное размытие и нарисовал результат на изображении края. Мое оригинальное тестовое изображение выглядит так:
В общем, моя конечная цель — обнаружить эллипс в сцене или на объекте. Что-то вроде этого:
Вот почему я решил использовать RANSAC, чтобы подогнать эллипс от краевых точек.
Что касается вашей конечной цели, вы можете попробовать
findContours а также [FitEllipse] в OpenCV
Псевдокод будет
1) некоторый процесс изображения
2) найти все контуры
3) подгонка каждого контура по fitEllipse
вот часть кода, который я использую до
[... image process ....you get a bwimage ]
vector<vector<Point> > contours;
findContours(bwimage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);for(size_t i = 0; i < contours.size(); i++)
{
size_t count = contours[i].size();
Mat pointsf;
Mat(contours[i]).convertTo(pointsf, CV_32F);
RotatedRect box = fitEllipse(pointsf);
/* You can put some limitation about size and aspect ratio here */
if( box.size.width > 20 &&
box.size.height > 20 &&
box.size.width < 80 &&
box.size.height < 80 )
{
if( MAX(box.size.width, box.size.height) > MIN(box.size.width, box.size.height)*30 )
continue;
//drawContours(SrcImage, contours, (int)i, Scalar::all(255), 1, 8);
ellipse(SrcImage, box, Scalar(0,0,255), 1, CV_AA);
ellipse(SrcImage, box.center, box.size*0.5f, box.angle, 0, 360, Scalar(200,255,255), 1, CV_AA);
}
}
imshow("result", SrcImage);
Вы используете термины необычным способом.
Обычно для изображений, термин «градиент» интерпретируется так, как будто изображение является математической функцией f(x,y)
, Это дает нам (df/dx, df/dy)
вектор в каждой точке.
Тем не менее, вы смотрите на изображение, как будто это функция y = f(x)
и градиент будет f(x)/dx
,
Теперь, если вы посмотрите на свое изображение, вы увидите, что две интерпретации определенно связаны. Ваш эллипс нарисован как набор контрастных пикселей, и в результате есть два резкие градиенты на изображении — внутренний и внешний. Они, конечно, соответствуют двум нормальным векторам и, следовательно, находятся в противоположных направлениях.
Также обратите внимание, что ваше изображение имеет пиксели. Градиент также является пиксельным. То, как рисуется ваш эллипс с шириной в один пиксель, означает, что ваш локальный градиент принимает только значения, кратные 45 градусам:
▄▄ ▄▀ ▌ ▀▄
Если вы сфокусировались на эллипсе (никакой другой фигуре), вы можете рассматривать значение пикселей эллипса как массу точек.
Затем вы можете вычислить момент инерции Ixx, Iyy, Ixy, чтобы узнать угол тета, который может повернуть общий эллипс обратно в каноническую форму (X-Xc) ^ 2 / a + (Y-Yc) ^ 2 / б = 1.
Тогда вы можете узнать Xc и Yc по центру масс.
Тогда вы можете узнать a и b по min X и min Y.
————— Обновить ————
Этот метод может применяться и к заполненному эллипсу.
Более одного эллипса на одном изображении потерпит неудачу, если вы сначала не сегментируете их.
Позвольте мне объяснить больше,
Я буду использовать C для обозначения cos (тэта) и S для представления греха (тэта)
После поворота в каноническую форму новый X равен [eq0] X = xC-yS, а Y равен Y = xS + yC, где x и y — исходные позиции.
Вращение даст вам мин IYY.
[EQ1]IYY = сумма (m * Y * Y) = сумма {m * (xS + yC)(xS + yC)} = Сумма {м(xxSS + yyCC + xySC) = Ixx * S ^ 2 + Iyy * C ^ 2 + Ixy * S * C
Для min IYY, d (IYY) / d (theta) = 0, то есть
2IxxSC — 2IyySC + Ixy (CC-SS) = 0
2 (Ixx-Iyy) / Ixy = (SS-CC) / SC = S / C + C / S = Z + 1 / Z
При программировании LHS это просто число, скажем, N
Z ^ 2 — NZ +1 = 0
Таким образом, есть два корня Z, следовательно, тета, скажем, Z1 и Z2, один из них будет минимальным IYY, а другой — максимальным IYY.
———— псевдокод ———
Вычислить Ixx, Iyy, Ixy для полого или заполненного эллипса.
Вычислите theta1 = atan (Z1) и theta2 = atan (Z2)
Поместите эти две тэты в находку eq1, которая меньше. Тогда вы получите тета.
Вернитесь к этим ненулевым пикселям, перенесите их в новые X и Y с помощью тэты, которую вы нашли.
Найти центр масс Xc Yc и min X и min Y по сортировке ().
————— рукой ————
Если вам нужно оригинальное уравнение эллипса
Просто поместите [eq0] в каноническую форму