Поверните неполное поле, чтобы оно было вертикальным - PullRequest
3 голосов
/ 05 февраля 2020

У меня есть набор данных рентгеновских изображений, которые я пытаюсь очистить, повернув изображения так, чтобы рука была в вертикальном положении и обрезав изображение любого лишнего пространства. Вот несколько примеров из набора данных:

scans

В настоящее время я разрабатываю лучший способ определить угол рентгеновского снимка и повернуть основанное на этом изображение.

Мой нынешний подход заключается в том, чтобы обнаружить линию стороны прямоугольника, в которой выполняется сканирование, с использованием преобразования Хафа, и повернуть изображение на основе этого.

I попытался выполнить грубое преобразование на выходе детектора контуров, но это не очень хорошо работает с изображениями, где край прямоугольника размыт, как на первом изображении.

Я не могу использовать определение поля cv, как иногда прямоугольник вокруг скана имеет край от края экрана.

Так что в настоящее время я использую адаптивный порог, чтобы найти край поля, а затем отфильтровать его по медиане и попытаться найти самую длинную строку в этом, но иногда неправильную линию является самым длинным, и изображение полностью поворачивается неправильно.

Адаптивный порог используется из-за того, что соевые сканы имеют разную яркость es.

Текущая реализация, которую я имею:

def get_lines(img):
  #threshold
  thresh = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 15, 4.75)
  median = cv2.medianBlur(thresh, 3)
  # detect lines
  lines = cv2.HoughLines(median, 1, np.pi/180, 175)
  return sorted(lines, key=lambda x: x[0][0], reverse=True)

def rotate(image, angle):
  (h, w) = image.shape[:2]
  (cX, cY) = (w // 2, h // 2)

  M = cv2.getRotationMatrix2D((cX, cY), angle, 1.0)
  cos = np.abs(M[0, 0])
  sin = np.abs(M[0, 1])

  nW = int((h * sin) + (w * cos))
  nH = int((h * cos) + (w * sin))

  M[0, 2] += (nW / 2) - cX
  M[1, 2] += (nH / 2) - cY

  return cv2.warpAffine(image, M, (nW, nH))

def fix_rotation(input):
  lines = get_lines(input)
  rho, theta = lines[0][0]
  return rotate_bound(input, theta*180/np.pi)

и дает следующие результаты:

intermediate results of current implementation

Когда что-то идет не так:

enter image description here

Мне было интересно, есть ли какие-нибудь лучшие методы, которые можно использовать для улучшения производительности этого и как лучше всего на go об обрезке изображений после их поворота будет?

Ответы [ 2 ]

3 голосов
/ 09 февраля 2020

Идея состоит в том, чтобы использовать шарик самой руки и накрыть ее эллипсом. Затем извлеките его главную ось. Я быстро проверил идею в Matlab - не в OpenCV. Вот что я сделал, вы должны быть в состоянии использовать эквивалентные функции OpenCV для достижения аналогичных результатов.

Сначала вычислите пороговое значение вашего входа через Otsu. Затем добавьте некоторое смещение к пороговому значению, чтобы найти лучшую сегментацию, и используйте это значение для порогового значения изображения.

В псевдокоде:

//the bias value
threshBias = 0.4;

//get the binary threshold via otsu: 
thresholdLevel = graythresh( grayInput, “otsu” );

//add bias to the original value
thresholdLevel = thresholdLevel - threshSensitivity * thresholdLevel;

//get the fixed binary image: 
thresholdLevel = imbinarize( grayInput, thresholdLevel );

После фильтрации небольших двоичных объектов это вывод:

enter image description here

Теперь получите контуры / капли и поместите эллипс для каждого контура. Посмотрите пример OpenCV здесь: https://docs.opencv.org/3.4.9/de/d62/tutorial_bounding_rotated_ellipses.html

В итоге вы получите два эллипса:

enter image description here

Мы ищем самый большой эллипс, самый большой с большой и малой осями. Я использовал ширину и высоту каждого эллипса для фильтрации результатов. Целевой эллипс затем окрашивается в зеленый цвет. Наконец, я получаю главную ось целевого эллипса, здесь окрашенного в желтый цвет:

enter image description here

Теперь, чтобы реализовать эти идеи в OpenCV у вас есть следующие опции:

  • Используйте fitEllipse, чтобы найти эллипсы. Возвращаемое значение этой функции - RotatedRect объект. Данные, хранящиеся здесь, являются вершинами эллипса.

  • Вместо подгонки к эллипсу вы можете попытаться использовать minAreaRect, который находит повернутый прямоугольник минимальной области, содержащей каплю.

1 голос
/ 07 февраля 2020

Вы можете использовать моменты изображения для расчета угла поворота. Используя функцию opencv моменты , рассчитайте центральные моменты второго порядка для построения ковариационной матрицы и затем получите ориентацию, как показано здесь на моменте изображения вики-странице. Получить нормализованные центральные моменты nu20, nu11 и nu02 из opencv moments. Затем ориентация рассчитывается как

0.5 * arctan(2 * nu11/(nu20 - nu02))

Для получения подробной информации см. Данную ссылку.

Вы можете использовать само необработанное изображение или предварительно обработанное изображение для расчета ориентации. Посмотрите, какой из них дает вам более высокую точность, и используйте его.

Что касается ограничивающего прямоугольника, то после поворота изображения, предположив, что вы использовали предварительно обработанный, получите все ненулевые пиксельные координаты повернутого изображения и рассчитать их вертикальный ограничивающий прямоугольник, используя opencv boundingRect .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...