Обнаружение переломов в руке с использованием обработки изображений

Что я наделал :

  1. Взято входное изображение и измененное изображение до стандартного размера

    поскольку я должен сравнить его с шаблоном.

  2. преобразованный в двоичном формате с использованием порогового значения.

  3. Обнаруженный подключенный компонент и отображаемый наибольший компонент.

    Поскольку это целая hand.as показывает здесь:

    IMG

  4. Поместите изображение в тех же координатах, чтобы проверить размещение пальца для сравнения с изображением шаблона

    но их позиционирование отличается от фиолетового – это шаблонное изображение

    IMG

  5. Я делаю сравнение изображения с методом вычитания изображений.

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

Их любой другой способ сделать это? Дайте мне знать, если они есть.

Здесь оригинальные необработанные изображения:

введите описание изображения здесь введите описание изображения здесь

Вы почти получили это, но то, что я имел в виду (из комментариев), было больше похоже на это:

  1. подготовить изображение

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

  2. выводить изображение как по оси x,y и создавать градиентное 2D-поле

    поэтому перекрасить изображение и создать 2D векторное поле. каждый пиксель имеет RGB, поэтому используйте R для одной оси и B для другого. Я делаю это вот так:

     Blue(x,y)=abs(Intensity(x,y)-Intensity(x-1,y)) Red (x,y)=abs(Intensity(x,y)-Intensity(x,y-1)) 

    Результат будет выглядеть следующим образом: края

  3. Изображение Treshold для подчеркивания ребер

    Таким образом, выберите каждый пиксель и сравните Blue(x,y)+Red(x,y)<treshold если истинный перекрашивание до неизвестного еще перекрашивается красным цветом. Для вашего образца изображения я использовал treshold 24 После этого сгладьте результат, чтобы заполнить небольшие промежутки размытым цветом. Результат будет выглядеть следующим образом: введите описание изображения здесь

    Зеленоватый материал – мой неизвестный цвет, а белые – края . Как вы видите, я размыл довольно много (слишком ленив для реализации подключенных компонентов).

  4. обнаружить фон

    Итак, теперь, чтобы отличить фон от костей внутри, я использую специальный метод заполнения (но простую заливку заливки сделает), которую я разработал для DIP- материала и нашел много полезного много раз по сравнению с прежними ожиданиями.

     void growfill(DWORD c0,DWORD c1,DWORD c2); // grow/flood fill c0 neigbouring c1 with c2 

    Который просто проверяет все пиксели изображения и, если найденный цвет c0 рядом с c1 затем перекрашивает его до c2 и цикл, пока не произойдет перекрашивание. Для большего разрешения обычно намного быстрее, чем заливка заливки из-за отсутствия необходимости в рекурсии или стек / кучи / списки. Также он может использоваться для многих интересных эффектов, таких как прореживание / утолщение и т. Д. С помощью простых звонков.

    Вернемся к теме. Я выбираю 3 основных цвета:

      //RRGGBB const DWORD col_unknown =0x00408020; // yet undetermined pixels const DWORD col_background=0x00000000; const DWORD col_edge =0x00FFFFFF; 

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

    После этого я перекрашу все пиксели, которые не являются одним из трех определенных цветов, в их ближайшем совпадении. Это позволит удалить размытие, поскольку это нежелательно больше. Результат будет выглядеть следующим образом: задний план

  5. Сегментация / маркировка

    Теперь просто сканируйте все изображение, и если какой-либо col_unknown будет найден, рост заполнит его цветом или индексом объекта. Измените фактический цвет объекта / индекс (приращение) и продолжайте до конца изображения. Остерегайтесь цветов, которые вы должны избегать использования трех предопределенных цветов, иначе вы объедините те области, которые вы не хотите.

    Окончательный результат выглядит следующим образом: скелет

  6. теперь вы можете применить любую форму анализа / сравнения

    у вас есть пиксельная маска для каждой области объекта, поэтому вы можете подсчитать пиксели (область) и удалить игнорировать слишком маленькие области. Вычислите позицию пикселя avg (в центре) каждого объекта и используйте это, чтобы определить, какая именно кость на самом деле. Вычислить однородность области … перемасштабировать до шаблонных костей … и т. Д. …

Вот код C ++, который я сделал с помощью

 color c,d; int x,y,i,i0,i1; int tr0=Form1->sb_treshold0->Position; // =24 treshold from scrollbar //RRGGBB const DWORD col_unknown =0x00408020; // yet undetermined pixels const DWORD col_background=0x00000000; const DWORD col_edge =0x00FFFFFF; // [prepare image] pic1=pic0; // copy input image pic0 to output pic1 pic1.pixel_format(_pf_u); // convert to grayscale intensity <0,765> pic1.enhance_range(); // recompute colors so they cover full dynamic range pic1.smooth(1); // blur a bit to remove noise // extract edges pic1.deriveaxy(); // compute derivations (change in intensity in x and y axis as 2D gradient vector) pic1.save("out0.png"); pic1.pf=_pf_rgba; // from now on the recolored image will be RGBA (no need for conversion) for (y=0;y<pic1.ys;y++) // treshold recolor for (x=0;x<pic1.xs;x++) { c=pic1.p[y][x]; i=c.dw[picture::_x]+c.dw[picture::_y]; // i=|dcolor/dx| + |dcolor/dy| if (i<tr0) c.dd=col_unknown; else c.dd=col_edge; // treshold test&recolor pic1.p[y][x]=c; } pic1.smooth(5); // blur a bit to fill the small gaps pic1.save("out1.png"); // [background] // render backround color rectangle around image pic1.bmp->Canvas->Pen->Color=rgb2bgr(col_background); pic1.bmp->Canvas->Brush->Style=bsClear; pic1.bmp->Canvas->Rectangle(0,0,pic1.xs,pic1.ys); pic1.bmp->Canvas->Brush->Style=bsSolid; // growth fill all col_unknonw pixels near col_background pixels with col_background similar to floodfill but without recursion and more usable. pic1.growfill(col_unknown,col_background,col_background); // recolor blured colors back to their closest match for (y=0;y<pic1.ys;y++) for (x=0;x<pic1.xs;x++) { c=pic1.p[y][x]; d.dd=col_edge ; i=abs(c.db[0]-d.db[0])+abs(c.db[1]-d.db[1])+abs(c.db[2]-d.db[2]); i0=i; i1=col_edge; d.dd=col_unknown ; i=abs(c.db[0]-d.db[0])+abs(c.db[1]-d.db[1])+abs(c.db[2]-d.db[2]); if (i0>i) { i0=i; i1=d.dd; } d.dd=col_background; i=abs(c.db[0]-d.db[0])+abs(c.db[1]-d.db[1])+abs(c.db[2]-d.db[2]); if (i0>i) { i0=i; i1=d.dd; } pic1.p[y][x].dd=i1; } pic1.save("out2.png"); // [segmentation/labeling] i=0x00202020; // labeling color/idx for (y=0;y<pic1.ys;y++) for (x=0;x<pic1.xs;x++) if (pic1.p[y][x].dd==col_unknown) { pic1.p[y][x].dd=i; pic1.growfill(col_unknown,i,i); i+=0x00050340; } pic1.save("out3.png"); - color c,d; int x,y,i,i0,i1; int tr0=Form1->sb_treshold0->Position; // =24 treshold from scrollbar //RRGGBB const DWORD col_unknown =0x00408020; // yet undetermined pixels const DWORD col_background=0x00000000; const DWORD col_edge =0x00FFFFFF; // [prepare image] pic1=pic0; // copy input image pic0 to output pic1 pic1.pixel_format(_pf_u); // convert to grayscale intensity <0,765> pic1.enhance_range(); // recompute colors so they cover full dynamic range pic1.smooth(1); // blur a bit to remove noise // extract edges pic1.deriveaxy(); // compute derivations (change in intensity in x and y axis as 2D gradient vector) pic1.save("out0.png"); pic1.pf=_pf_rgba; // from now on the recolored image will be RGBA (no need for conversion) for (y=0;y<pic1.ys;y++) // treshold recolor for (x=0;x<pic1.xs;x++) { c=pic1.p[y][x]; i=c.dw[picture::_x]+c.dw[picture::_y]; // i=|dcolor/dx| + |dcolor/dy| if (i<tr0) c.dd=col_unknown; else c.dd=col_edge; // treshold test&recolor pic1.p[y][x]=c; } pic1.smooth(5); // blur a bit to fill the small gaps pic1.save("out1.png"); // [background] // render backround color rectangle around image pic1.bmp->Canvas->Pen->Color=rgb2bgr(col_background); pic1.bmp->Canvas->Brush->Style=bsClear; pic1.bmp->Canvas->Rectangle(0,0,pic1.xs,pic1.ys); pic1.bmp->Canvas->Brush->Style=bsSolid; // growth fill all col_unknonw pixels near col_background pixels with col_background similar to floodfill but without recursion and more usable. pic1.growfill(col_unknown,col_background,col_background); // recolor blured colors back to their closest match for (y=0;y<pic1.ys;y++) for (x=0;x<pic1.xs;x++) { c=pic1.p[y][x]; d.dd=col_edge ; i=abs(c.db[0]-d.db[0])+abs(c.db[1]-d.db[1])+abs(c.db[2]-d.db[2]); i0=i; i1=col_edge; d.dd=col_unknown ; i=abs(c.db[0]-d.db[0])+abs(c.db[1]-d.db[1])+abs(c.db[2]-d.db[2]); if (i0>i) { i0=i; i1=d.dd; } d.dd=col_background; i=abs(c.db[0]-d.db[0])+abs(c.db[1]-d.db[1])+abs(c.db[2]-d.db[2]); if (i0>i) { i0=i; i1=d.dd; } pic1.p[y][x].dd=i1; } pic1.save("out2.png"); // [segmentation/labeling] i=0x00202020; // labeling color/idx for (y=0;y<pic1.ys;y++) for (x=0;x<pic1.xs;x++) if (pic1.p[y][x].dd==col_unknown) { pic1.p[y][x].dd=i; pic1.growfill(col_unknown,i,i); i+=0x00050340; } pic1.save("out3.png"); 

Я использую свой собственный класс изображений для изображений, поэтому некоторые участники:

  • xs,ys Размер изображения в пикселях
  • p[y][x].dd – пиксель в позиции (x,y) как 32-битный целочисленный тип
  • p[y][x].dw[2] – пиксель в позиции (x,y) как целочисленный тип 2×16 бит для 2D- полей
  • p[y][x].db[4] – пиксель в позиции (x,y) как целочисленный тип 4×8 бит для простого доступа к каналу
  • clear(color) – очищает все изображение
  • resize(xs,ys) – изменяет resize(xs,ys) изображения до нового разрешения
  • bmpVCL инкапсулированный GDI Bitmap с доступом Canvas
  • smooth(n) – быстрое размытие изображения n раз
  • growfill(DWORD c0,DWORD c1,DWORD c2) – растить / заливать заливку c0 neigbouring c1 c2

Обнаружение костей на основе линии сканирования [Edit1]

Как и в связанном круге поисковых горизонтов, вы должны отображать линии сканирования и искать отличную функцию, распознающую кость. Я бы начал с частичного вывода изображения (по оси x), как этот:

линия сканирования

Слева – вывод интенсивности цвета по x (серый означает нуль) и правое исходное изображение. Боковые графы – это графы деривации как функция от x и y взятые для строки и строки фактической позиции мыши. Как вы можете видеть, каждая кость имеет четкую форму в деривации, которая может быть обнаружена. Я использовал очень простой детектор:

  1. для обработанной линии изображения выполняется частичный вывод по x
  2. найти все пики (круги)
  3. удалить слишком маленькие пики и объединить одинаковые знаковые пики вместе
  4. обнаруживают кость по своим 4-мя последующим пикам:
    1. большой отрицательный
    2. небольшой положительный
    3. небольшой отрицательный
    4. большой положительный

Для каждого найденного края кости я делаю красный и синий пиксели в исходном изображении (на месте больших пиков), чтобы визуально проверить правильность. Вы тоже делаете это по оси Y одинаковым образом и объединяете результаты. Чтобы улучшить это, вы должны использовать лучшее обнаружение, например, используя корреляцию …

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

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