公司搞了一个项目。给阿里闲鱼做的。要求把一张图片用很多口红拼图显示出来。然后就只能够撸一发二值化算法了
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.cnblogs.com/carekee/articles/3643394.html 13种全局二值化带C#代码。很叼