2

我的数学有点生疏。我正在尝试均衡表示 0-255 范围内的灰度值的二维数组的直方图(由于它们的计算方式,值可能不是整数)。

我在 Wikipedia 上找到了这篇文章,但我不太了解它们提供的公式。

ni,我可以计算nL但我不太确定如何实现这个cdf函数。这个功能可能有用吗?

这是我到目前为止所得到的:

static double[,] Normalize(double[,] mat)
{
    int width = mat.GetLength(0);
    int height = mat.GetLength(1);
    int nPixels = width*height;

    double sum = 0;
    double max = double.MinValue;
    double min = double.MaxValue;
    var grayLevels = new Dictionary<double, int>();

    foreach (var g in mat)
    {
        sum += g;
        if (g > max) max = g;
        if (g < min) min = g;
        if (!grayLevels.ContainsKey(g)) grayLevels[g] = 0;
        ++grayLevels[g];
    }
    double avg = sum/nPixels;
    double range = max - min;

    var I = new double[width,height];

    // how to normalize?

    return I;
}
4

6 回答 6

3

找到了一些你可能会觉得有用的东西

http://sonabstudios.blogspot.in/2011/01/histogram-equalization-algorithm.html

希望有帮助

于 2013-05-22T04:14:00.980 回答
2

计算累积分布函数涉及几个步骤。

首先你得到你的灰度值的频率分布。

所以像:

freqDist = new int[256];

for each (var g in mat)
{
    int grayscaleInt = (int)g;
    freqDist[grayscaleInt]++;
}

然后得到你的 CDF,比如:

cdf = new int[256];
int total = 0;

for (int i = 0; i < 256; i++)
{
    total += freqDist[i];
    cdf[i] = total;
}
于 2013-05-22T04:25:38.257 回答
2

这是我的实现:

private static byte[,] Normalize(byte[,] mat)
{
    int width = mat.GetLength(0);
    int height = mat.GetLength(1);
    int nPixels = width*height;

    var freqDist = new int[256];

    foreach (var g in mat)
    {
        ++freqDist[g];
    }

    var cdf = new int[256];
    int total = 0;

    for (int i = 0; i < 256; ++i)
    {
        total += freqDist[i];
        cdf[i] = total;
    }

    int cdfmin = 0;

    for (int i = 0; i < 256; ++i)
    {
        if (cdf[i] > 0)
        {
            cdfmin = cdf[i];
            break;
        }
    }

    var I = new byte[width,height];
    double div = (nPixels - cdfmin) / 255d;

    for (int y = 0; y < height; ++y)
    {
        for (int x = 0; x < width; ++x)
        {
            I[x, y] = (byte)Math.Round((cdf[mat[x, y]] - cdfmin) / div);
        }
    }

    return I;
}

我将其从使用双精度数更改为字节数,以便更好地使用直方图 ( freqDist)。

于 2014-06-24T17:54:16.170 回答
2

我可以帮助你理解你的链接

首先,代表图像的计数值显示在该链接中,

Value   Count   Value   Count   Value   Count   Value   Count   Value   Count
   52       1      64       2      72       1      85       2     113       1
   55       3      65       3      73       2      87       1     122       1
   58       2      66       2      75       1      88       1     126       1
   59       3      67       1      76       1      90       1     144       1
   60       1      68       5      77       1      94       1     154       1
   61       4      69       3      78       1     104       2   
   62       1      70       4      79       2     106       1
   63       2      71       2      83       1     109       1

这意味着,图像是使用上述值创建的,仅此而已。

其次,将值从 52 累加到 154

Value   cdf Value   cdf Value   cdf Value   cdf Value   cdf
   52     1    64    19    72    40    85    51   113    60
   55     4    65    22    73    42    87    52   122    61
   58     6    66    24    75    43    88    53   126    62
   59     9    67    25    76    44    90    54   144    63
   60    10    68    30    77    45    94    55   154    64
   61    14    69    33    78    46   104    57 
   62    15    70    37    79    48   106    58
   63    17    71    39    83    49   109    59

这是手段,

value 52 have 1 cdf cause it is initial value, 
value 55 have 4 cdf cause it has 3 count in image plus 1 cdf from 52, 
value 58 have 6 cdf cause it has 2 count in image plus 4 cdf from 55,
and so on.. till..
value 154 have 64 cdf cause it has 1 count in image plus 63 cdf from 144.

然后,根据函数计算每个图像值的直方图均衡公式

cdf(v) 代表当前图像值的当前 cdf 值,

在这种情况下,如果是h(v) = 61这样cdf(v) = 14

cdfmin 表示初始 cdf 值,在这种情况下,1 cdf 从值 52

快乐编码.. ^^

于 2013-05-22T04:49:38.563 回答
1

你可以使用我刚刚写的这个函数:

public static Bitmap ContrastStretch(Bitmap srcImage, double blackPointPercent = 0.02, double whitePointPercent = 0.01)
{
    BitmapData srcData = srcImage.LockBits(new Rectangle(0, 0, srcImage.Width, srcImage.Height), ImageLockMode.ReadOnly,
        PixelFormat.Format32bppArgb);
    Bitmap destImage = new Bitmap(srcImage.Width, srcImage.Height);
    BitmapData destData = destImage.LockBits(new Rectangle(0, 0, destImage.Width, destImage.Height),
        ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
    int stride = srcData.Stride;
    IntPtr srcScan0 = srcData.Scan0;
    IntPtr destScan0 = destData.Scan0;
    var freq = new int[256];

    unsafe
    {
        byte* src = (byte*) srcScan0;
        for (int y = 0; y < srcImage.Height; ++y)
        {
            for (int x = 0; x < srcImage.Width; ++x)
            {
                ++freq[src[y*stride + x*4]];
            }
        }

        int numPixels = srcImage.Width*srcImage.Height;
        int minI = 0;
        var blackPixels = numPixels*blackPointPercent;
        int accum = 0;

        while (minI < 255)
        {
            accum += freq[minI];
            if (accum > blackPixels) break;
            ++minI;
        }

        int maxI = 255;
        var whitePixels = numPixels*whitePointPercent;
        accum = 0;

        while (maxI > 0)
        {
            accum += freq[maxI];
            if (accum > whitePixels) break;
            --maxI;
        }
        double spread = 255d/(maxI - minI);
        byte* dst = (byte*) destScan0;
        for (int y = 0; y < srcImage.Height; ++y)
        {
            for (int x = 0; x < srcImage.Width; ++x)
            {
                int i = y*stride + x*4;

                byte val = (byte) Clamp(Math.Round((src[i] - minI)*spread), 0, 255);
                dst[i] = val;
                dst[i + 1] = val;
                dst[i + 2] = val;
                dst[i + 3] = 255;
            }
        }
    }

    srcImage.UnlockBits(srcData);
    destImage.UnlockBits(destData);

    return destImage;
}

static double Clamp(double val, double min, double max)
{
    return Math.Min(Math.Max(val, min), max);
}

默认值意味着最暗的 2% 像素将变为黑色,最亮的 1% 将变为白色,并且中间的所有内容都将被拉伸以填充颜色空间。这与ImageMagick的默认值相同。

这个算法有一个有趣的副作用,如果你使用高于 50% 的值,那么它会反转图像!设置为 .5, .5 以获得黑白图像(2 种色调)或 1, 1 以获得完美的反转。

假设您的图像已经是 grayscale

于 2014-06-13T06:47:34.630 回答
1

除了 John 所说的之外,您还需要使用 cdf 数组来计算每个像素的新值。您可以通过以下方式执行此操作:

  • 调整 John 的第二次迭代以获得第一个具有 a 的 i freqDist > 0并调用 i imin
  • 在 0 和宽度以及 0 和高度之间逐个像素地去像素 i,j 并评估round((cdf[pixel[i,j]]-cdf[imin])/(width*height-cdf[imin]))*255),这是该位置的归一化像素值。
于 2013-05-22T04:39:16.140 回答