Як «нармалізаваць» чорна-белы малюнак?

Мая матэматыка трохі іржавы. Я спрабую выраўнаваць гістаграму 2D масіва, які ўяўляе значэнне адценняў шэрага ў дыяпазоне 0-255 (значэння не могуць быць цэлымі лікамі, так як пра тое, як яны будуць палічаныя).

Я знайшоў гэтага артыкула у Вікіпедыі, але я не зусім разумею формулы яны прадстаўляюць.

ni, n and L I can compute, but I'm not quite sure how to implement this cdf function. Might this function be of use?

Вось што ў мяне да гэтага часу:

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();

    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;
}
1

6 адказы

Знойдзена тое, што вы маглі б знайсці карысным

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

Надзея, што дапамагае

3
дададзена
Гэта каментар, не адказ.
дададзена аўтар Blender, крыніца
Я не магу каментаваць, але пакуль я хацеў дапамагчы.
дададзена аўтар Hanut, крыніца

Вось мая рэалізацыя:

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 ).

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;
}
2
дададзена

Вы можаце выкарыстоўваць гэтую функцыю, я проста напісаў:

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 .

This algorithm has the fun side effect that if you use values above 50% then it will invert the image! Set to .5, .5 to get a black & white image (2 shades) or 1, 1 to get a perfect inversion.

Маецца на ўвазе ваша выява ўжо паўтонавыя .

1
дададзена
Хуткасць. Гэта было даўно, але я лічу, што гэта вельмі павольна, каб атрымаць доступ да дадзеных пікселя ў бяспечным кантэксце.
дададзена аўтар mpen, крыніца
Дзякуй за размяшчэнне назад свае вынікі. Любая прычына, за выгоды для небяспечнага сферы?
дададзена аўтар slater, крыніца

У дадатак да таго, што кажа Джон, вам трэба будзе выкарыстоўваць ВПР масіў, каб вылічыць новае значэнне для кожнага пікселя. Вы можаце зрабіць гэта:

  • Adjust John's second iteration to get the first i that has a freqDist > 0 and call that i imin
  • Going pixel by pixel i,j between 0 and width and 0 and height repectively and evaluating round((cdf[pixel[i,j]]-cdf[imin])/(width*height-cdf[imin]))*255), that is the normalized pixel value at that location.
1
дададзена
Абодва вы адказвалі карысныя, але ў вас быў хітрэйшы частку. Зразумеў рэалізуецца ў цяперашні час, здаецца, працуе добра. Дзякуй!
дададзена аўтар mpen, крыніца

Я магу дапамагчы вам зразумець спасылачныя ,

па-першае, лічачы значэнне, якое ўяўляюць малюнак, паказвае ў гэтай спасылцы,

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) з'яўляецца ўяўляюць сабой бягучае значэнне ВПР ад бягучага значэння малюнка,

У гэтым выпадку, калі ч (V) = 61 так ВПР (v) = 14

cdfmin гэта ўяўляюць пачатковае значэнне ВПР, у дадзеным выпадку, 1 ВПР ад значэння 52

шчаслівае кадаваньне .. ^^

1
дададзена