寻找图像中的局部极大点

  某个图像处理的任务需求被转化为一个寻找图像局部最大值的问题,例如下面的图像是一段来自医学投影的组织影像,其中用肉眼能够辨识出一些灰度值比较大的团块,这些团块都是代表着一定医学含义的组织。需求是找出类似这种团块的个数。

原图 需要找的团块

  后来在ImageJ中发现了一个这个比较奇妙的算法,叫做FindMaxima,这个算法支持提供一个ErorrTolerance参数之后,根据这个参数来调整寻找到极大位置的个数。例如选择ErorrTolerance为下面三个不同的数值后,算出来的团块数是不同的。

IJ中的算法 算法参数面板 算法结果

 

作者的描述

  根据ImageJ中提供的信息可以找到这个算法的原作者,叫做Michael Schmid,在论坛上可以找到他对自己写的这个算法的简单描述:

Hi, 

sorry, there is no publication on it (at least none that I am aware of, 

probably I have reinvented the wheel), but it is rather simple code: 

(1) Find the local maxima 

(2) Sort them in descending sequence 

(3) For each local maximum, do a flood fill algorithm with the gray level 

tolerance (without modifying the original, it is done on a temporary 

scratch image). Maxima where flood filling reaches a previously filled 

area (i.e., area of other maximum within the tolerance) are discarded. 

(4) In case of 'single points' output, if there are several points having 

the highest value inside the flood-filled area, use the one that is 

closest to their geometric center. 

 

Segmentation is a bit more difficult (it stems from older ImageJ 

versions), and I am currently about to see whether it can't be done faster 

and with better accuracy... 

 ------Michael 

  总结这个描述,可以按照如下思路简单的实现这个算法:

  1. 寻找到所有比8邻域像素值都大的像素点,存入数组L
  2. 对L按像素值从高到低排序
  3. 对每一个L中的像素P
    1. 以P为种子点执行泛洪法,包含原则为与P的像素值相差在tolerance之内,若遇到比P像素值更高的点或者遇到了被标记为Maxima的点,则此P点不为Maxima,否则P被标记为Maxima。
  4. 将L中标记为Maxima的体素输出

 

根据描述实现一个轻量版

  根据上述思路用C#实现一个版本,其代码如下:

复制代码
public struct Int16Double
{
    public int X;
    public int Y;
    public Int16Double(int x, int y)
    {
        X = x;
        Y = y;
    }
}
public struct Int16DoubleWithValue:IComparable<Int16DoubleWithValue>
{
    public int X;
    public int Y;
    public float V;
    public Int16DoubleWithValue(int x, int y, float value)
    {
        X = x;
        Y = y;
        V = value;
    }

    public int CompareTo(Int16DoubleWithValue other)
    {
        return -this.V.CompareTo(other.V);
    }
}
public class BitMap2d
{
    public float[] data;
    public int width;
    public int height;
    public BitMap2d(int width, int height, float v)
    {
        this.width = width;
        this.height = height;
        data = new float[width * height];
        for (int i = 0; i < width * height; i++)
            data[i] = v;
    }
    public void SetPixel(int x, int y, byte v)
    {
        data[x + y * width] = v;
    }
    public float GetPixel(int x, int y)
    {
        return data[x + y * width];
    }
    public void ReadRaw(string path)
    {
        FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
        BinaryReader sr = new BinaryReader(fs);

        for (int i = 0; i < width * height; i++)
        {
            byte[] floatBytes = sr.ReadBytes(4);

            // swap the bytes

            byte temp = floatBytes[0];

            floatBytes[0] = floatBytes[3];

            floatBytes[3] = temp;

            temp = floatBytes[1];

            floatBytes[1] = floatBytes[2];

            floatBytes[2] = temp;

            // get the float from the byte array

            float value = BitConverter.ToSingle(floatBytes, 0);
            data[i] = value;
        }
        sr.Close();
        fs.Close();
        return;
    }
    public Bitmap MakeBmp()
    { 
        float min=float.MaxValue;
        float max=float.MinValue;
        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                    float r=this.GetPixel(i,j);
                if(r>max)
                    max=r;
                if(r<min)
                    min=r;
            }
        }
        float delta=max-min;
        Bitmap bmp = new Bitmap(this.width, this.height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                float r=this.GetPixel(i,j);
                int b=(int)(255*(r-min)/delta);
                Color c=Color.FromArgb((byte)b,(byte)b,(byte)b);
                bmp.SetPixel(i, j, c);
            }
        }
        return bmp;
    }
}
public class FlagMap2d
{
    public int action_set_count;
    public int action_get_count;
    public int width;
    public int height;
    byte[] flags;
    public FlagMap2d(int width, int height,byte v)
    {
        this.width = width;
        this.height = height;
        action_get_count = 0;
        action_set_count = 0;
        flags = new byte[width * height];
        for(int i=0;i<width*height;i++)
            flags[i] = v;
    }
    public void SetFlagOn(int x, int y, byte v)
    {
        flags[x + y * width] = v;
        action_set_count++;
    }
    public byte GetFlagOn(int x, int y)
    {
        action_get_count++;
        return flags[x + y * width];
    }
}
复制代码
复制代码
public class MaximunFinder
{
    BitMap2d bmp;
    float torlerance;
    const byte UNPROCESSED = 0;
    const byte VISITED = 1;
    const byte PROCESSED = 2;
    static Int16Double[] Delta = new Int16Double[8]
    {
        new Int16Double(-1,-1),
        new Int16Double(-1,0),
        new Int16Double(-1,1),
        new Int16Double(0,-1),
        new Int16Double(0,1),
        new Int16Double(1,-1),
        new Int16Double(1,0),
        new Int16Double(1,1),
    };

    public MaximunFinder(BitMap2d bmp,float torlerance)
    {
        this.bmp = bmp;
        this.torlerance = torlerance;
    }
    public List<Int16DoubleWithValue> FindMaxima()
    {
        List<Int16DoubleWithValue> list = FindLocalMaxima();
        list.Sort();
        FlagMap2d flag=new FlagMap2d(bmp.width,bmp.height,0);
        List<Int16DoubleWithValue> r=new List<Int16DoubleWithValue>();
        List<Int16Double> temp=new List<Int16Double>();
        for (int i = 0; i < list.Count; i++)
        {
            if (flag.GetFlagOn(list[i].X, list[i].Y) == UNPROCESSED)
            {
                bool ret = FloodFill(list[i].X, list[i].Y,temp,flag);
                if (ret)
                {
                    r.Add(list[i]);
                        
                    MarkAll(temp, PROCESSED, flag);
                }
                else
                {
                    MarkAll(temp, UNPROCESSED, flag);
                    flag.SetFlagOn(list[i].X, list[i].Y, PROCESSED);
                }
                temp.Clear();
            }
        }
        return r;
    }

    private List<Int16DoubleWithValue> FindLocalMaxima()
    {
        List<Int16DoubleWithValue> list = new List<Int16DoubleWithValue>();
        for (int i = 1; i < bmp.width - 1; i++)
        {
            for (int j = 1; j < bmp.height - 1; j++)
            {
                if (IsMaxima(i, j))
                {
                    list.Add(new Int16DoubleWithValue(i, j,bmp.GetPixel(i,j)));
                }
            }
        }
        return list;
    }

    private bool IsMaxima(int i, int j)
    {
        float v = bmp.GetPixel(i, j);
        bool b1 = v > bmp.GetPixel(i - 1, j - 1);
        bool b2 = v > bmp.GetPixel(i, j - 1);
        bool b3 = v > bmp.GetPixel(i +1, j - 1);

        bool b4 = v > bmp.GetPixel(i - 1, j);
        bool b5 = v > bmp.GetPixel(i + 1, j);

        bool b6 = v > bmp.GetPixel(i - 1, j + 1);
        bool b7 = v > bmp.GetPixel(i, j + 1);
        bool b8 = v > bmp.GetPixel(i + 1, j + 1);
        return b1 && b2 && b3 && b4 && b5 && b6 && b7 && b8;
    }

    private bool FloodFill(int x, int y,List<Int16Double> ret,FlagMap2d flag)
    {
        ret.Clear();
        Queue<Int16Double> queue = new Queue<Int16Double>();
        ret.Add(new Int16Double(x, y));
        float pvalue = bmp.GetPixel(x, y);
        flag.SetFlagOn(x, y, VISITED);
        queue.Enqueue(new Int16Double(x, y));
        while (queue.Count != 0)
        {
            Int16Double p = queue.Dequeue();
            for (int i = 0; i < 8; i++)
            {
                int tx = p.X + Delta[i].X;
                int ty = p.Y + Delta[i].Y;
                if(InRange(tx, ty))
                {
                    byte f= flag.GetFlagOn(tx,ty);
                    if(f==PROCESSED)
                        return false;
                    else
                    {
                        bool minum = false;
                        if (IncludePredicate(tx, ty, pvalue,ref minum) && f == UNPROCESSED)
                        {
                            if (minum)
                                return false;
                            Int16Double t = new Int16Double(tx, ty);
                            queue.Enqueue(t);
                            flag.SetFlagOn(tx, ty, VISITED);
                            ret.Add(t);
                        }
                    }

                }
            }
        }
        return true;
    }

    private bool InRange(int tx, int ty)
    {
        return tx >= 0 && tx < bmp.width && ty >= 0 && ty < bmp.height;
    }

    private bool IncludePredicate(int x, int y, float pv, ref bool min)
    {
        float v = bmp.GetPixel(x, y);
        if (pv < v)
            min = true;
        return pv - v <= torlerance;
    }

    private void MarkAll(List<Int16Double> ret, byte v,FlagMap2d flag)
    {
        for (int i = 0; i < ret.Count; i++)
        {
            flag.SetFlagOn(ret[i].X, ret[i].Y, v);
        }
    }

}
复制代码

 

 

算法输出预览

  原图和输出图像如下表所示:

 
原图 本文实现的输出 IJ的输出

   代码地址:https://github.com/chnhideyoshi/SeededGrow2d/tree/master/FindMaxima

Maxima快速参考手册 命令手册 帮助 pdf 目录 1 基本介绍 4 1.1 一历史 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.2 启动和退出 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.3 在线帮助 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.4 数据类型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2 初等数学 8 2.1 算术运算 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.2 常用初等函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.3 预定义常数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.4 自定义函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.5 求和与求积 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.6 代数运算与化简 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.6.1 多项式展开 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.6.2 因式分解 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.6.3 等量代换 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.6.4 分式展开 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.6.5 分式化简 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.6.6 对数、指数及根式化简 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.6.7 条件假设 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.7 三角函数变换 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 *Maxima快速参考手册 by Huan Ma is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. +Copyright ?c 2010–2011 Huan Ma. 欢迎反馈:yusufma77@yahoo.com 1 2.7.1 常用变换 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.7.2 控制变量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.8 解方程 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.8.1 单个方程 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.8.2 方程组 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.8.3 数值解 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.9 复数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.9.1 实部与虚部 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.9.2 复共轭 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.9.3 复数模和辐角 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.9.4 直角形式和极坐标形式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3 矩阵 17 3.1 矩阵输入 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.1.1 交互式输入 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.1.2 以列表形式输入 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.1.3 以函数形式输入 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.1.4 对角矩阵 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.1.5 单一非零元素矩阵 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2 矩阵运算 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2.1 加减乘除 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2.2 幂运算 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.2.3 矩阵乘法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.3 行(列)操作 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.3.1 提取行(列) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.3.2 增加行(列) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.3.3 子矩阵 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.4 线性代数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.4.1 行列式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.4.2 矩阵转置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.4.3 矩阵的逆 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.4.4 矩阵的秩 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3.4.5 高斯消元 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.4.6 本征值和本征向量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4 微积分 23 4.1 微分 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.2 积分 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.2.1 不定积分 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 4.2.2 定积分 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2 4.2.3 数值积分 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 4.3 泰勒展开 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.4 拉普拉斯变换 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.5 留数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 5 微分方程 27 5.1 一阶或二阶常微分方程通解 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 5.2 初值问题 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 5.3 边值问题 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 5.4 一阶线性微分方程组 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 6 特殊函数 29 7 作图 30 7.1 二维作图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 7.1.1 一般函数作图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 7.1.2 对数坐标图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 7.1.3 参数方程作图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 7.1.4 数据作图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 7.1.5 存为图片文件 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 7.2 三维作图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 8 图形界面 35 8.1 wxMaxima . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 8.2 xmaxima . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 8.3 TeXmacs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 8.4 Emacs+imaxima . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 9 Maxima编程 39 9.1 do循环 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 9.2 if条件句 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 9.3 block程序块 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 9.4 read读取 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 9.5 结果输出 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 9.5.1 二维表示 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 9.5.2 输出到文件 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 9.6 随机数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 9.7 batch执行程序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 9.8 tex输出 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 索引 43 3
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值