在unity中做图片二值化的一些方法。C#

公司搞了一个项目。给阿里闲鱼做的。要求把一张图片用很多口红拼图显示出来。然后就只能够撸一发二值化算法了

1.图片二值化主要分两种一种叫做全局二值化,一种叫做局部二值化

全局二值化 是指用整张图片所有像素的灰度值算出一个全局的阈值,然后在用所有像素的灰度值和这个阈值比较,如果大于这个阈值就是黑小于这个就是白。这个做法做出来的图片相对来说比较 丰满。但是对于那些图片灰度分布不均匀的图片效果就很不理想了

局部二值化  是指把图片分成很多小块对每一块的图片做阈值比较。这样的方法可以适应更多类型的图片。

在全局二值化中 大津法可以说是标杆,而在局部二值化中 Sauvola算法可以算是标杆。
废话不说上代码C#代码。这是王宇大神折腾出来的大 津法

int FinalValue;
float finalValueFloat;  
void Otsu(Texture2D texTemp)
    {
        int width = texTemp.width;
        int height = texTemp.height;
        float[] nHistogram = new float[256];//灰度直方图  
        float[] dVariance = new float[256];//类间方差
        int N = width * height;//总像素数
        for (int i = 0; i < 256; i++)
        {
            nHistogram[i] = 0.0f;
            dVariance[i] = 0.0f;
        }
        Color pc = texTemp.GetPixel(0, height - 1);
        Debug.Log(texTemp.width + "  " + texTemp.height);
        string msg = "";
        float g = 0;
        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                g = texTemp.GetPixel(i, j).grayscale;
                int temp = (int)Math.Round(g * 255);
                nHistogram[temp]++;//建立直方图
            }
        }
        float Pa = 0.0f;      //背景出现概率  
        float Pb = 0.0f;      //目标出现概率  
        float Wa = 0.0f;      //背景平均灰度值  
        float Wb = 0.0f;      //目标平均灰度值  
        float W0 = 0.0f;      //全局平均灰度值  
        float dData1 = 0.0f;
        float dData2 = 0.0f;
        //计算全局平均灰度 
        for (int i = 0; i < 256; i++)
        {
            nHistogram[i] /= N;
            W0 += i * nHistogram[i];
        }
        scale = -0.008f * W0 + 2.5f;
        //对每个灰度值计算类间方差  
        for (int i = 0; i < 256; i++)
        {
            Pa += nHistogram[i];
            Pb = 1 - Pa;
            dData1 += i * nHistogram[i];
            dData2 = W0 - dData1;
            Wa = dData1 / Pa;
            Wb = dData2 / Pb;
            dVariance[i] = (Pa * Pb * Mathf.Pow((Wb - Wa), 2));
        }
        //遍历每个方差,求取类间最大方差所对应的灰度值 
        float temp2 = 0f;
        for (int i = 0; i < 256; i++)
        {
            if (dVariance[i] > temp2)
            {
                temp2 = dVariance[i];
                FinalValue = i;
                finalValueFloat = FinalValue / 255f;
            }
        }
    }

finalValueFloat 既是全局得出来的阈值。可以根据这个阈值和图像的灰度值做比较,大于这个阈值为黑,小于为白

局部的计算稍微复杂一些。
1.首先要把灰度值从第一个像素全部磊加到最后一个,Sauvola还的把灰度平方后累加。
2.定好每个小块的宽高
3.循环每个像素点,以像素点为中心,宽高就是2步骤的宽高取一个方块。然后获得这个方块的所有灰度值,和所有灰度方差值
通过这两个可以计算出这个方块的阈值 
其中m(x,y)为当前方块平均灰度,s(x,y)为当前灰度标准方差
R为标准方差的动态范围,如果输入图像为8位的灰度图像则 R=128.我用的是256.因为我觉得我的图像质量比较高
K是使用者自定义的一个修正参数,k的取值范围一般来说是0<k<1.可以用来调节二值化画出来的大小的流量,
上代码

grayImage数组为我把一个位图的所有灰度全部取出来的(方法如下) trueTex为Unity中的Texture2D
float[]  grayImage new   float [trueTex.width * trueTex.height];
       int tw = trueTex.width;
       int th = trueTex.height;
       for (int i = 0; i < tw; i++)
       {
           for (int j = 0; j < th; j++)
           {
                gryarr [j * tw + i] = trueTex.GetPixel(i, j).grayscale;
           }
       }  

void Csauvola(float[] grayImage, int w, int h, float k, int windowSize)
    {
        int whalf = windowSize >> 1;
        //windowSize = 1;
        int i, j;
        int IMAGE_WIDTH = w;
        int IMAGE_HEIGHT = h;
        // create the integral image
        float[] integralImg = new float[IMAGE_WIDTH * IMAGE_HEIGHT];
        float[] integralImgSqrt = new float[IMAGE_WIDTH * IMAGE_HEIGHT];
        float sum = 0;
        float sqrtsum = 0;
        int index;
        StringBuilder sbs = new StringBuilder();
        int ids = 0;
        for (i = 0; i < IMAGE_HEIGHT; i++)
        {
            sum = 0;
            sqrtsum = 0;
            for (j = 0; j < IMAGE_WIDTH; j++)
            {
                //index = (IMAGE_HEIGHT - 1 - i) * IMAGE_WIDTH + j;
                index = i * IMAGE_WIDTH + j;
                sum += grayImage[index];
                sqrtsum += grayImage[index] * grayImage[index];
                ids += 1;
                sbs.AppendLine("g=" + grayImage[index] + "sum=" + sum + "sqrtsum=" + sqrtsum + "index=" + ids);
                if (i == 0)
                {
                    integralImg[index] = sum;
                    integralImgSqrt[index] = sqrtsum;
                }
                else
                {
                    integralImgSqrt[index] = integralImgSqrt[(i - 1) * IMAGE_WIDTH + j] + sqrtsum;
                    integralImg[index] = integralImg[(i - 1) * IMAGE_WIDTH + j] + sum;
                }
            }
        }
        int xmin, ymin, xmax, ymax;
        float mean, std, threshold;
        float diagsum, idiagsum, diff, sqdiagsum, sqidiagsum, sqdiff, area;
        Color[] biImage = new Color[IMAGE_WIDTH * IMAGE_HEIGHT];
        for (i = 0; i < IMAGE_WIDTH; i++)
        {
            for (j = 0; j < IMAGE_HEIGHT; j++)
            {
                xmin = Mathf.Max(0, i - whalf);
                ymin = Mathf.Max(0, j - whalf);
                xmax = Mathf.Min(IMAGE_WIDTH - 1, i + whalf);
                ymax = Mathf.Min(IMAGE_HEIGHT - 1, j + whalf);
                area = (xmax - xmin + 1) * (ymax - ymin + 1);
                if (area <= 0)
                {
                    biImage[i * IMAGE_WIDTH + j] = Color.clear;//255;
                    continue;
                }
                if (xmin == 0 && ymin == 0)
                {
                    diff = integralImg[ymax * IMAGE_WIDTH + xmax];
                    sqdiff = integralImgSqrt[ymax * IMAGE_WIDTH + xmax];
                }
                else if (xmin > 0 && ymin == 0)
                {
                    diff = integralImg[ymax * IMAGE_WIDTH + xmax] - integralImg[ymax * IMAGE_WIDTH + xmin - 1];
                    sqdiff = integralImgSqrt[ymax * IMAGE_WIDTH + xmax] - integralImgSqrt[ymax * IMAGE_WIDTH + xmin - 1];
                }
                else if (xmin == 0 && ymin > 0)
                {
                    diff = integralImg[ymax * IMAGE_WIDTH + xmax] - integralImg[(ymin - 1) * IMAGE_WIDTH + xmax];
                    sqdiff = integralImgSqrt[ymax * IMAGE_WIDTH + xmax] - integralImgSqrt[(ymin - 1) * IMAGE_WIDTH + xmax]; ;
                }
                else
                {
                    diagsum = integralImg[ymax * IMAGE_WIDTH + xmax] + integralImg[(ymin - 1) * IMAGE_WIDTH + xmin - 1];
                    idiagsum = integralImg[(ymin - 1) * IMAGE_WIDTH + xmax] + integralImg[ymax * IMAGE_WIDTH + xmin - 1];
                    diff = diagsum - idiagsum;
                    sqdiagsum = integralImgSqrt[ymax * IMAGE_WIDTH + xmax] + integralImgSqrt[(ymin - 1) * IMAGE_WIDTH + xmin - 1];
                    sqidiagsum = integralImgSqrt[(ymin - 1) * IMAGE_WIDTH + xmax] + integralImgSqrt[ymax * IMAGE_WIDTH + xmin - 1];
                    sqdiff = sqdiagsum - sqidiagsum;
                }
                //灰度和平均值
                mean = diff / area;
                //标准方差。
                std = Mathf.Sqrt((sqdiff - diff * diff / area) / (area - 1));
                //阈值
                threshold = mean * (1 + k * ((std / 255) - 1));
                //当前像素灰度
                float golys = grayImage[j * IMAGE_WIDTH + i];
                //用当前像素灰度和当前阈值做比较
                if (grayImage[j * IMAGE_WIDTH + i] < threshold)
                {
                    biImage[j * IMAGE_WIDTH + i] = Color.white * 0;//0;
                }
                else
                {
                    biImage[j * IMAGE_WIDTH + i] = Color.white;//255;
                }
                //索贝尔算出图片边缘
                int iw = IMAGE_WIDTH;
                float gx = 0;
                float gy = 0;
                int addline = 1;
                if (j - addline < 0 || j + addline >= IMAGE_HEIGHT || i - addline < 0 || i + addline >= iw)
                {
                    biImage[j * IMAGE_WIDTH + i] = Color.clear;
                    continue;
                }
                gx = grayImage[(j - addline) * iw + i + addline] + 2 * grayImage[(j) * iw + i + addline] + grayImage[(j + addline) * iw + i + addline] - (grayImage[(j - addline) * iw + i - addline] + 2 * grayImage[(j) * iw + i - addline] + grayImage[(j + 1) * iw + i - addline]);
                gy = grayImage[(j - addline) * iw + i - addline] + 2 * grayImage[(j - addline) * iw + i] + grayImage[(j - addline) * iw + i + addline] - (grayImage[(j + addline) * iw + i - addline] + 2 * grayImage[(j + addline) * iw + i] + grayImage[(j + addline) * iw + i + addline]);
                float g = Mathf.Sqrt(Mathf.Pow(gx, 2) + Mathf.Pow(gy, 2));
                if (golys < g)
                {
                    biImage[j * iw + i] = Color.clear;
                }
            }
        }
    }  

为来让图片更加丰满在代码里面用索贝尔算出图片边缘

当然还有很多其他的二值化算法可以参考其他链接
http://blog.csdn.net/tyf122/article/details/8738156  这个是列举一些局部和全局二值化的目录
http://www.lxway.com/498114011.htm   这个是 Sauvola 算法,但是他下面还有很多相关文章很有价值
http://www.cnblogs.com/carekee/articles/3643394.html  13种全局二值化带C#代码。很叼 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值