【问题标题】:Fracture detection in hand using image proccessing使用图像处理进行手部骨折检测
【发布时间】:2016-08-21 06:04:52
【问题描述】:

我做了什么:

  1. 获取输入图像并将图像调整为标准尺寸

    因为我必须将它与模板进行比较。

  2. 使用阈值转换为二进制。

  3. 检测到连通分量并显示最大分量​​。

    因为它是整只手。如下所示:

  4. 将图像放置在相同坐标以检查手指的位置以与模板图像进行比较

    但它们的定位不同,紫色的一个是模板图像

  5. 我正在用图像减法方法比较图像。

这些案例无法预测它们是否是发际线断裂,因为它们在图像中检测到许多细线。

他们还有其他方法吗?让我知道他们是否有。

这里是未经处理的原始图像:

【问题讨论】:

  • 您能否也分享一些处理过的包含发际线断裂的图像,以及原始图像。而且我认为从模板进行比较不会产生任何解决方案,因为每个图像都会有所不同。
  • 你也有源未处理的图像吗?我认为您应该分割单个骨骼并比较它们而不是整只手......如果出现断裂,您也可以尝试忽略模板并计算内表面平滑度,表面将不均匀。
  • 感谢您在下方回复@Mayank 我已附上发际线断裂的图片
  • 感谢您在下方回复@Spektre 我已附上未处理的图像
  • 这些未处理的原始断裂图像dropbox.com/s/qkxexbdh7kezuiv/Fracture%20in%20hand.JPEG?dl=0 发际线断裂的原始图像dropbox.com/s/ytaqi338360bvt1/… 使用连接组件标记分割发际线断裂的图像dropbox.com/s/drps1e3d4019n3x/hairline.jpg?dl=0

标签: php image-processing feature-detection


【解决方案1】:

你几乎明白了,但我的想法(来自 cmets)更像是这样的:

  1. 准备图片

    切换到灰度、去除噪点(通过一些模糊)、增强动态范围等。

  2. 通过两个x,y 轴导出图像并创建渐变二维场

    所以重新着色图像并创建 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. 阈值图像强调边缘

    因此选择每个像素并比较 Blue(x,y)+Red(x,y)<treshold 如果为真则重新着色为 unknown 否则重新着色为 edge 颜色。对于您的示例图像,我使用了阈值24 之后平滑结果以用模糊的颜色填充小间隙。子结果如下所示:

    绿色是我的未知颜色,白色是边缘。如您所见,我模糊了很多(懒得实现连接的组件)。

  4. 检测背景

    所以现在为了区分背景和里面的骨头,我使用了特殊的填充方法(但一个简单的填充就可以了)我为 DIP 开发的东西,发现它非常有用,比以前的预期好很多倍。

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

    它只是检查图像中的所有像素,如果在c1 附近找到颜色c0,然后将其重新着色为c2 并循环直到没有发生重新着色。由于不需要递归或堆栈/堆/列表,更大的分辨率通常比洪水填充快得多。它还可以用于许多很酷的效果,如变薄/加厚等,只需几个简单的调用即可。

    好的,回到主题我选择3种基色:

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

    现在背景肯定是在边缘周围,所以我用 col_background 在图像周围绘制矩形,并用col_background 填充col_background 附近的所有col_unknown 像素,这基本上从外到内填充图像。

    在此之后,我将所有不是 3 种定义颜色中的任何一种的像素重新着色为最接近的匹配。这将消除模糊,因为它不再是可取的。子结果如下所示:

  5. 分割/标签

    现在只需扫描整个图像,如果发现任何col_unknown 增长,则用对象不同的颜色/索引填充它。更改实际对象的不同颜色/索引(增量)并继续直到图像结束。请注意您必须避免使用 3 种预定颜色的颜色,否则您会合并您不想要的区域。

    最终结果如下所示:

  6. 现在您可以应用任何形式的分析/比较

    您获得了每个对象区域的像素掩码,因此您可以计算像素(区域)并删除忽略太小的区域。计算每个对象的平均像素位置(中心)并使用它来检测它实际上是哪个骨骼。计算区域的同质性......重新缩放到模板骨骼......等等......

这是我使用的一些 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");

我将自己的图片类用于图像,所以一些成员是:

  • xs,ys 图像大小(以像素为单位)
  • p[y][x].dd(x,y) 位置的像素,为 32 位整数类型
  • p[y][x].dw[2](x,y) 位置的像素,为 2D 字段的 2x16 位整数类型
  • p[y][x].db[4](x,y) 位置的像素,为 4x8 位整数类型,便于通道访问
  • clear(color) - 清除整个图像
  • resize(xs,ys) - 将图像调整为新分辨率
  • bmp - VCL 封装了 GDI 带有 Canvas 访问的位图
  • smooth(n) - 快速模糊图像n
  • growfill(DWORD c0,DWORD c1,DWORD c2) - 生长/洪水填充c0 neigbouring c1c2

[Edit1] 基于扫描线的骨骼检测

在链接的 find horizo​​n QA 中,您必须投射扫描线并搜索识别骨骼的不同特征。我将从像这样的图像(在 x 轴上)的部分推导开始::

左边是x的颜色强度推导(灰色表示零),右边是原始图像。侧图是作为xy 的函数的推导图,用于实际鼠标位置的行和行。正如您所看到的,每个骨骼在推导中都有不同的形状,可以检测到。我使用了一个非常简单的检测器,如下所示:

  1. 对于已处理的图像行,通过x 进行部分推导
  2. 找到所有峰(圆圈)
  3. 移除太小的峰并将相同符号的峰合并在一起
  4. 通过 4 个后续峰检测骨骼:
    1. 大负面
    2. 小正
    3. 小负数
    4. 大正面

对于每个找到的骨骼边缘,我在原始图像中渲染红色和蓝色像素(在大峰的位置),以直观地检查正确性。您也可以在 y 轴上以相同的方式执行此操作并合并结果。为了改善这一点,您应该使用更好的检测,例如通过使用相关性...

您可以轻松地创建骨骼蒙版,然后将其分割为单独的骨骼并按照上述文本进行处理,而不是边缘渲染。您还可以使用形态学运算来填补任何空白。

我能想到的最后一件事是为骨骼的关节侧添加一些检测(形状不同)。它需要大量的试验,但至少你知道该走哪条路。

【讨论】:

  • 感谢您的努力。但是他们在拇指中检测到的部位太多,其余手指和手的下半部分几乎检测到异类。
  • Hello Spetre 是他们单独分割骨骼的另一种方法。现在正在尝试去除肉Your solution
  • 检测到水平线Horizontal line in hand
  • @JYOTIRAJAI 抱歉,直到现在有一​​段时间没有时间/心情。我在 [edit1] 中添加了这种方法描述和我的尝试示例。我觉得效果还不错。
猜你喜欢
  • 2015-11-09
  • 2021-05-27
  • 2012-02-28
  • 2015-11-06
  • 1970-01-01
  • 2021-02-13
  • 1970-01-01
  • 2015-02-02
  • 1970-01-01
相关资源
最近更新 更多