emgucv处理图像 查找轮廓个数

有时想起 2016-08-25 04:55:15
输入图像经二值后如图所示
现在用的方法是: 检测凸包个数,用Contour<Point>.Convex属性。凸缺陷也试了,用Contour<Point>.GetConvexityDefacts。但是两个查出来的正确率都比较低。有时候13 有时候12 、11、求问有何其他好方法
...全文
958 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_39580335 2017-07-21
  • 打赏
  • 举报
回复
二值化,凸包,缺陷可以教教我吗,加我1026608284
有时想起 2016-08-31
  • 打赏
  • 举报
回复
引用 8 楼 crystal_lz 的回复:

    rect.Width += 1; rect.Height += 1;
    if (nCount < nPixelMin || nCount > nPixelMax) {
        foreach (var v in lstTemp) {
            this.SetColorFormByColorInfo(v.X, v.Y, Color.White);
        }
        return Rectangle.Empty;
    }
看到上面的图 为什么没有圈出来的地方是白色的 我就觉得奇怪了 然后我刚才突然发现 为什么 里面会有这个循环 这不是脑残了吗 多次一句不说 还浪费时间 浪费的不只是一个循环的时间 由于被设置成了白色 下次遍历到的时候 遇到白色 又会去判断一次 因为我是一行一行遍历的 遇到一个白色 就去找和这个白色连通的像素点 这代码删掉吧 把那个循环注释掉后 我电脑20毫秒左右出结果 不注释 接近200毫秒 我就不知道 当时写代码时候 脑子是不是短路了。。。
嗯 可以的 就是对白块断点的话 判断还是差点 本来一个会圈出2个
有时想起 2016-08-30
  • 打赏
  • 举报
回复
引用 3 楼 crystal_lz 的回复:
你是要白色的个数? 而且你的图都张差不多这个样子 ?
是的 白色个数 图都差不多的 区别就是旋转了 位置不一样罢了
crystal_lz 2016-08-30
  • 打赏
  • 举报
回复

    rect.Width += 1; rect.Height += 1;
    if (nCount < nPixelMin || nCount > nPixelMax) {
        foreach (var v in lstTemp) {
            this.SetColorFormByColorInfo(v.X, v.Y, Color.White);
        }
        return Rectangle.Empty;
    }
看到上面的图 为什么没有圈出来的地方是白色的 我就觉得奇怪了 然后我刚才突然发现 为什么 里面会有这个循环 这不是脑残了吗 多次一句不说 还浪费时间 浪费的不只是一个循环的时间 由于被设置成了白色 下次遍历到的时候 遇到白色 又会去判断一次 因为我是一行一行遍历的 遇到一个白色 就去找和这个白色连通的像素点 这代码删掉吧 把那个循环注释掉后 我电脑20毫秒左右出结果 不注释 接近200毫秒 我就不知道 当时写代码时候 脑子是不是短路了。。。
crystal_lz 2016-08-30
  • 打赏
  • 举报
回复
是的

这个是我 nPixelMin 200 的效果
有时想起 2016-08-30
  • 打赏
  • 举报
回复
引用 5 楼 crystal_lz 的回复:
参照我以前一个验证码识别的帖子 里面的代码 有一个 CodeHelper.cs http://bbs.csdn.net/topics/391905849 注释里面也是有的 可以参照这张图的思路 深度遍历 连续的像素点 这个是我从里面拷贝出来的部分代码 如果按照你的需求 可以去掉一些代码

private void button1_Click(object sender, EventArgs e) {
    Image img = Image.FromFile("./fuck.png");
    List<Rectangle> lstRect = this.GetCharRect(img, 0, int.MaxValue);
    using (Graphics g = this.CreateGraphics()) {
        g.DrawImage(img, 0, 0);
        lstRect.ForEach(r => {
            g.DrawRectangle(Pens.Cyan, r);
        });
    }
}
private BitmapData m_bmpData;
private byte[] m_byColorInfo;
/// <summary>
/// 获取验证码字符所在的区域
/// </summary>
/// <param name="imgDark">二值化后的图像</param>
/// <param name="nPixelMin">连续的最小像素个数 小于此数将被忽略</param>
/// <param name="nPixelMax">连续的最大像素个数 大于此数将被忽略</param>
/// <returns></returns>
public List<Rectangle> GetCharRect(Image imgDark, int nPixelMin, int nPixelMax) {
    Bitmap bmp = (Bitmap)imgDark;
    m_bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    m_byColorInfo = new byte[bmp.Height * m_bmpData.Stride];
    Marshal.Copy(m_bmpData.Scan0, m_byColorInfo, 0, m_byColorInfo.Length);
    List<Rectangle> lstRect = new List<Rectangle>();
    for (int y = 0, leny = bmp.Height; y < leny; y++) {
        for (int x = 0, lenx = bmp.Width; x < lenx; x++) {
            if (this.GetArgbFormByColor(x, y) == Color.White.ToArgb()) {//【【【注意 你需要的是白色】】】
                Rectangle rectTemp = this.GetRectFromPoint(nPixelMin, nPixelMax, new Point(x, y), Color.White, Color.Magenta);
                if (rectTemp != Rectangle.Empty) lstRect.Add(rectTemp);
            }
        }
    }
    Marshal.Copy(m_byColorInfo, 0, m_bmpData.Scan0, m_byColorInfo.Length);
    bmp.UnlockBits(m_bmpData);
    //将区域按照left属性排序
    for (int i = 0; i < lstRect.Count; i++) {
        for (int j = 1; j < lstRect.Count - i; j++) {
            if (lstRect[j - 1].Left > lstRect[j].Left) {
                Rectangle rectTemp = lstRect[j];
                lstRect[j] = lstRect[j - 1];
                lstRect[j - 1] = rectTemp;
            }
        }
    }
    return lstRect;
}

private Rectangle GetRectFromPoint(int nPixelMin, int nPixelMax, Point ptStart, Color clrSrc, Color clrSet) {
    int nCount = 0;
    int nArgb = clrSrc.ToArgb();
    Rectangle rect = new Rectangle(ptStart.X, ptStart.Y, 0, 0);
    List<Point> ptRegList = new List<Point>();
    List<Point> lstTemp = new List<Point>();
    ptRegList.Add(ptStart);
    lstTemp.Add(ptStart);
    this.SetColorFormByColorInfo(ptStart.X, ptStart.Y, clrSet);
    while (ptRegList.Count != 0) {
        Point ptTemp = this.GetNextPoint(ptRegList[ptRegList.Count - 1], nArgb);
        //Point ptTemp = ptRegList[ptRegList.Count - 1];
        if (ptTemp != Point.Empty) {
            ptRegList.Add(ptTemp);
            lstTemp.Add(ptTemp);
            this.SetColorFormByColorInfo(ptTemp.X, ptTemp.Y, clrSet);
            nCount++;
            if (ptTemp.X < rect.Left) { rect.Width = rect.Right - ptTemp.X; rect.X = ptTemp.X; }
            if (ptTemp.Y < rect.Top) { rect.Height = rect.Bottom - ptTemp.Y; rect.Y = ptTemp.Y; }
            if (ptTemp.X > rect.Right) rect.Width = ptTemp.X - rect.Left;
            if (ptTemp.Y > rect.Bottom) rect.Height = ptTemp.Y - rect.Top;
        } else
            ptRegList.RemoveAt(ptRegList.Count - 1);
    }
    rect.Width += 1; rect.Height += 1;
    if (nCount < nPixelMin || nCount > nPixelMax) {
        foreach (var v in lstTemp) {
            this.SetColorFormByColorInfo(v.X, v.Y, Color.White);
        }
        return Rectangle.Empty;
    }
    return rect;
}

private Point GetNextPoint(Point ptStart, int nArgb) {
    if (m_bmpData.Height > ptStart.Y + 1 && this.GetArgbFormByColor(ptStart.X, ptStart.Y + 1) == nArgb)
        return new Point(ptStart.X, ptStart.Y + 1);
    if (m_bmpData.Width > ptStart.X + 1 && this.GetArgbFormByColor(ptStart.X + 1, ptStart.Y) == nArgb)
        return new Point(ptStart.X + 1, ptStart.Y);
    if (0 <= ptStart.Y - 1 && this.GetArgbFormByColor(ptStart.X, ptStart.Y - 1) == nArgb)
        return new Point(ptStart.X, ptStart.Y - 1);
    if (0 <= ptStart.X - 1 && this.GetArgbFormByColor(ptStart.X - 1, ptStart.Y) == nArgb)
        return new Point(ptStart.X - 1, ptStart.Y);
    if (0 <= ptStart.X - 1 && m_bmpData.Height > ptStart.Y + 1 && this.GetArgbFormByColor(ptStart.X - 1, ptStart.Y + 1) == nArgb)
        return new Point(ptStart.X - 1, ptStart.Y + 1);
    if (m_bmpData.Width > ptStart.X + 1 && m_bmpData.Height > ptStart.Y + 1 && this.GetArgbFormByColor(ptStart.X + 1, ptStart.Y + 1) == nArgb)
        return new Point(ptStart.X + 1, ptStart.Y + 1);
    if (m_bmpData.Width > ptStart.X + 1 && 0 <= ptStart.Y - 1 && this.GetArgbFormByColor(ptStart.X + 1, ptStart.Y - 1) == nArgb)
        return new Point(ptStart.X + 1, ptStart.Y - 1);
    if (0 <= ptStart.X - 1 && 0 <= ptStart.Y - 1 && this.GetArgbFormByColor(ptStart.X - 1, ptStart.Y - 1) == nArgb)
        return new Point(ptStart.X - 1, ptStart.Y - 1);
    return Point.Empty;
}

private int GetArgbFormByColor(int x, int y) {
    return Color.FromArgb(255,
         m_byColorInfo[y * m_bmpData.Stride + x * 3],
         m_byColorInfo[y * m_bmpData.Stride + x * 3 + 1],
         m_byColorInfo[y * m_bmpData.Stride + x * 3 + 2]).ToArgb();
}

private void SetColorFormByColorInfo(int x, int y, Color clr) {
    m_byColorInfo[y * m_bmpData.Stride + x * 3] = clr.B;
    m_byColorInfo[y * m_bmpData.Stride + x * 3 + 1] = clr.G;
    m_byColorInfo[y * m_bmpData.Stride + x * 3 + 2] = clr.R;
}
测试了 很好用 识别率也可以 就是这个nPixelMin参数好像效果看不出 我试着慢慢加大。主要有一些图片白块连接的地方有点断。出来的效果其实是一个白块 但会误识别两个。是应该调整这里的nPixelMin参数么???
crystal_lz 2016-08-30
  • 打赏
  • 举报
回复

参照我以前一个验证码识别的帖子 里面的代码 有一个 CodeHelper.cs
http://bbs.csdn.net/topics/391905849
注释里面也是有的

可以参照这张图的思路 深度遍历 连续的像素点
这个是我从里面拷贝出来的部分代码 如果按照你的需求 可以去掉一些代码

private void button1_Click(object sender, EventArgs e) {
Image img = Image.FromFile("./fuck.png");
List<Rectangle> lstRect = this.GetCharRect(img, 0, int.MaxValue);
using (Graphics g = this.CreateGraphics()) {
g.DrawImage(img, 0, 0);
lstRect.ForEach(r => {
g.DrawRectangle(Pens.Cyan, r);
});
}
}
private BitmapData m_bmpData;
private byte[] m_byColorInfo;
/// <summary>
/// 获取验证码字符所在的区域
/// </summary>
/// <param name="imgDark">二值化后的图像</param>
/// <param name="nPixelMin">连续的最小像素个数 小于此数将被忽略</param>
/// <param name="nPixelMax">连续的最大像素个数 大于此数将被忽略</param>
/// <returns></returns>
public List<Rectangle> GetCharRect(Image imgDark, int nPixelMin, int nPixelMax) {
Bitmap bmp = (Bitmap)imgDark;
m_bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
m_byColorInfo = new byte[bmp.Height * m_bmpData.Stride];
Marshal.Copy(m_bmpData.Scan0, m_byColorInfo, 0, m_byColorInfo.Length);
List<Rectangle> lstRect = new List<Rectangle>();
for (int y = 0, leny = bmp.Height; y < leny; y++) {
for (int x = 0, lenx = bmp.Width; x < lenx; x++) {
if (this.GetArgbFormByColor(x, y) == Color.White.ToArgb()) {//【【【注意 你需要的是白色】】】
Rectangle rectTemp = this.GetRectFromPoint(nPixelMin, nPixelMax, new Point(x, y), Color.White, Color.Magenta);
if (rectTemp != Rectangle.Empty) lstRect.Add(rectTemp);
}
}
}
Marshal.Copy(m_byColorInfo, 0, m_bmpData.Scan0, m_byColorInfo.Length);
bmp.UnlockBits(m_bmpData);
//将区域按照left属性排序
for (int i = 0; i < lstRect.Count; i++) {
for (int j = 1; j < lstRect.Count - i; j++) {
if (lstRect[j - 1].Left > lstRect[j].Left) {
Rectangle rectTemp = lstRect[j];
lstRect[j] = lstRect[j - 1];
lstRect[j - 1] = rectTemp;
}
}
}
return lstRect;
}

private Rectangle GetRectFromPoint(int nPixelMin, int nPixelMax, Point ptStart, Color clrSrc, Color clrSet) {
int nCount = 0;
int nArgb = clrSrc.ToArgb();
Rectangle rect = new Rectangle(ptStart.X, ptStart.Y, 0, 0);
List<Point> ptRegList = new List<Point>();
List<Point> lstTemp = new List<Point>();
ptRegList.Add(ptStart);
lstTemp.Add(ptStart);
this.SetColorFormByColorInfo(ptStart.X, ptStart.Y, clrSet);
while (ptRegList.Count != 0) {
Point ptTemp = this.GetNextPoint(ptRegList[ptRegList.Count - 1], nArgb);
//Point ptTemp = ptRegList[ptRegList.Count - 1];
if (ptTemp != Point.Empty) {
ptRegList.Add(ptTemp);
lstTemp.Add(ptTemp);
this.SetColorFormByColorInfo(ptTemp.X, ptTemp.Y, clrSet);
nCount++;
if (ptTemp.X < rect.Left) { rect.Width = rect.Right - ptTemp.X; rect.X = ptTemp.X; }
if (ptTemp.Y < rect.Top) { rect.Height = rect.Bottom - ptTemp.Y; rect.Y = ptTemp.Y; }
if (ptTemp.X > rect.Right) rect.Width = ptTemp.X - rect.Left;
if (ptTemp.Y > rect.Bottom) rect.Height = ptTemp.Y - rect.Top;
} else
ptRegList.RemoveAt(ptRegList.Count - 1);
}
rect.Width += 1; rect.Height += 1;
if (nCount < nPixelMin || nCount > nPixelMax) {
foreach (var v in lstTemp) {
this.SetColorFormByColorInfo(v.X, v.Y, Color.White);
}
return Rectangle.Empty;
}
return rect;
}

private Point GetNextPoint(Point ptStart, int nArgb) {
if (m_bmpData.Height > ptStart.Y + 1 && this.GetArgbFormByColor(ptStart.X, ptStart.Y + 1) == nArgb)
return new Point(ptStart.X, ptStart.Y + 1);
if (m_bmpData.Width > ptStart.X + 1 && this.GetArgbFormByColor(ptStart.X + 1, ptStart.Y) == nArgb)
return new Point(ptStart.X + 1, ptStart.Y);
if (0 <= ptStart.Y - 1 && this.GetArgbFormByColor(ptStart.X, ptStart.Y - 1) == nArgb)
return new Point(ptStart.X, ptStart.Y - 1);
if (0 <= ptStart.X - 1 && this.GetArgbFormByColor(ptStart.X - 1, ptStart.Y) == nArgb)
return new Point(ptStart.X - 1, ptStart.Y);
if (0 <= ptStart.X - 1 && m_bmpData.Height > ptStart.Y + 1 && this.GetArgbFormByColor(ptStart.X - 1, ptStart.Y + 1) == nArgb)
return new Point(ptStart.X - 1, ptStart.Y + 1);
if (m_bmpData.Width > ptStart.X + 1 && m_bmpData.Height > ptStart.Y + 1 && this.GetArgbFormByColor(ptStart.X + 1, ptStart.Y + 1) == nArgb)
return new Point(ptStart.X + 1, ptStart.Y + 1);
if (m_bmpData.Width > ptStart.X + 1 && 0 <= ptStart.Y - 1 && this.GetArgbFormByColor(ptStart.X + 1, ptStart.Y - 1) == nArgb)
return new Point(ptStart.X + 1, ptStart.Y - 1);
if (0 <= ptStart.X - 1 && 0 <= ptStart.Y - 1 && this.GetArgbFormByColor(ptStart.X - 1, ptStart.Y - 1) == nArgb)
return new Point(ptStart.X - 1, ptStart.Y - 1);
return Point.Empty;
}

private int GetArgbFormByColor(int x, int y) {
return Color.FromArgb(255,
m_byColorInfo[y * m_bmpData.Stride + x * 3],
m_byColorInfo[y * m_bmpData.Stride + x * 3 + 1],
m_byColorInfo[y * m_bmpData.Stride + x * 3 + 2]).ToArgb();
}

private void SetColorFormByColorInfo(int x, int y, Color clr) {
m_byColorInfo[y * m_bmpData.Stride + x * 3] = clr.B;
m_byColorInfo[y * m_bmpData.Stride + x * 3 + 1] = clr.G;
m_byColorInfo[y * m_bmpData.Stride + x * 3 + 2] = clr.R;
}
有时想起 2016-08-29
  • 打赏
  • 举报
回复
求解答呀。。。。。。。。。。。。。。。。。。。。。
crystal_lz 2016-08-29
  • 打赏
  • 举报
回复
你是要白色的个数? 而且你的图都张差不多这个样子 ?
有时想起 2016-08-26
  • 打赏
  • 举报
回复
呀 在线等啊
一、主要内容:OpenCV能够实现强大丰富的图像处理,但是它缺少一个能够支持它运行的界面。Csharp经过多年的发展,得益于它的“所见及所得”能力,非常方便编写界面。这两者如果能够“双剑合璧”,将有效帮助实际工作产出。本课着重推荐GOCW采用“Csharp基于CLR直接调用Opencv编写的算法库”方法,能够将最新的OpenCV技术引入进来,同时保证生成程序的最小化。    为了进一步说明Csharp和OpenCV的结合使用,首先一个较为完整的基于winform实现答题卡识别的例子,相比较之前的实现,本次进一步贴近生产实际、内涵丰富,对算法也进行了进一步提炼。同时我们对WPF下对OpenCV函数的调用、OpenCV.js的调用进行相关教授。       二、课程结构1、 EmguCV、OpenCVSharp和GOCW之间进行比较(方便代码编写、能够融入最新的算法、速度有保障、方便调试找错、拒绝黑箱化);2、视频采集模块的构建,视频采集和图像处理之间的关系;3、视频采集专用的SDK和“陪练”系统的介绍;4、在视频增强类项目中和图像处理项目中,算法的选择;5、Csharp界面设计、图片的存储和其他构建设计;6、较为完整的答题卡识别例子,兼顾界面设计和算法分析;8、WPF基于GOCW也同样可以基于GOCW实现算法调用;webForm虽然也可以通过类似方法调用,但是OpenCV.JS的方法更现代高效。9、关于软件部署的相关要点和窍门。       三、知识要点:1、基本环境构建和程序框架;2、CLR基本原理和应用方法;3、接入、采集、模拟输入;4、图像处理,通过构建循环采集图片;5、增强和实时处理;6、基于投影等技术的答题卡识别算法;7、存储、转换;8、部署交付。        课程能够帮助你掌握Csharp调用Opencv的基本方法,获得相应框架代码和指导;从而进一步提升实现“基于图像处理”的解决方案能力。  

110,539

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧