拖延症又犯了, 上周第一篇: 机器学习系列之: 怎么样数鸡 ? (1) 说到了先把图片转成灰度 (Grey Scale), 接下来我们要做的就是 计算阈值 (Threshold)
当我们进行图片灰度化的时候, 我们把 RGBA 图片每个相素4个字节转成了 亮度 1个字节, 用了以下公式:
亮度 = R * 0.21 + G * 0.72 + B * 0.07
但是这对于后面要分类还是不太方便, 我们理想只需要 0 和1 , 1代表是鸡, 0代表是空气, 所以这一步计算阈值就是要计算出一个值, 然后之后根据这个 阈值 (Threshold) 来二分成 0 或 1. 比如 我们已经计算得到 threshold 然后可以遍历图片的每个相素, 依次按 RGB 三个值的平均计算得到当前的密度(Intensity) 和这个阀值判断二分成前景和背景(鸡和空气) :
本文的C#代码来自于 微软大神 Gary Short.
大津算法 Otsu’s Method
大津算法(Otsu’s Method)是计算机图形学上用来把一个灰度图片退化为黑白(二值)图像. 具体的算法可以看 WIKI, 这里就不多说明了.
假设以下方法用于得到这个阀值.
1 | private static int GetThreshold(Bitmap image) |
private static int GetThreshold(Bitmap image)
参数为图片(灰度), 首先我们得把图片变成字节数组(每个相素为1个字节8位)
1 | byte[] imageBytes = (byte[])new ImageConverter().ConvertTo(image, typeof(byte[])); |
byte[] imageBytes = (byte[])new ImageConverter().ConvertTo(image, typeof(byte[]));
然后这个大津算法需要图片的 Histogram (直方图), 横坐标是0到255个格子, 纵坐标是出现的次数.
1 2 3 4 5 6 7 | int[] histogram = new int[256]; int ptr = 0; while (ptr < imageBytes.Length) { int h = 0xFF & imageBytes[ptr]; histogram[h]++; ptr++; } |
int[] histogram = new int[256]; int ptr = 0; while (ptr < imageBytes.Length) { int h = 0xFF & imageBytes[ptr]; histogram[h]++; ptr++; }
把出现的次数计算成概率:
1 2 3 | int totalPixels = imageBytes.Length; // 相素点数 float sumOfIntensities = 0; for (int t = 0; t < 256; t++) sumOfIntensities += t * histogram[t]; |
int totalPixels = imageBytes.Length; // 相素点数 float sumOfIntensities = 0; for (int t = 0; t < 256; t++) sumOfIntensities += t * histogram[t];
接下来就是大津算法得到这个阀值使得两类内方差(Variance)最小, 也就是类间(Intra Class)方差最大
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | // 穷举阀值得到能使内间类别区别最大的值 float sumOfBackgroundIntensities = 0; int backgroundWeight = 0; int foregroundWeight = 0; float maximumInterClassVariance = 0; int threshold = 0; for (int t = 0; t < 256; t++) { backgroundWeight += histogram[t]; if (backgroundWeight == 0) continue; foregroundWeight = totalPixels - backgroundWeight; if (foregroundWeight == 0) break; sumOfBackgroundIntensities += (float)(t * histogram[t]); float backgroundMean = sumOfBackgroundIntensities / backgroundWeight; float foregroundMean = (sumOfIntensities - sumOfBackgroundIntensities) / foregroundWeight; // 计算内间方差 float intraClassVariance = (float)backgroundWeight * (float)foregroundWeight * (backgroundMean - foregroundMean) * (backgroundMean - foregroundMean); // 更新最大类间方差值 if (intraClassVariance > maximumInterClassVariance) { maximumInterClassVariance = intraClassVariance; threshold = t; } } return threshold; |
// 穷举阀值得到能使内间类别区别最大的值 float sumOfBackgroundIntensities = 0; int backgroundWeight = 0; int foregroundWeight = 0; float maximumInterClassVariance = 0; int threshold = 0; for (int t = 0; t < 256; t++) { backgroundWeight += histogram[t]; if (backgroundWeight == 0) continue; foregroundWeight = totalPixels - backgroundWeight; if (foregroundWeight == 0) break; sumOfBackgroundIntensities += (float)(t * histogram[t]); float backgroundMean = sumOfBackgroundIntensities / backgroundWeight; float foregroundMean = (sumOfIntensities - sumOfBackgroundIntensities) / foregroundWeight; // 计算内间方差 float intraClassVariance = (float)backgroundWeight * (float)foregroundWeight * (backgroundMean - foregroundMean) * (backgroundMean - foregroundMean); // 更新最大类间方差值 if (intraClassVariance > maximumInterClassVariance) { maximumInterClassVariance = intraClassVariance; threshold = t; } } return threshold;
计得上周的这个鸡图么?
来, 课后作业, 留言告诉我, 这张图的 阈值 是多少, 前三名答对的每人奖励 3 SBD.
未完待续……
- 机器学习系列之: 怎么样数鸡?
- 机器学习系列之: 怎么样数鸡鸡? 大津算法来计算阈值
- 机器学习系列之: 怎么样数鸡鸡? 分类 Clustering
强烈推荐
- 英国代购-畅购英伦
- TopCashBack 返现 (英国购物必备, 积少成多, 我2年来一共得了3000多英镑)
- Quidco 返现 (也是很不错的英国返现网站, 返现率高)
- 注册就送10美元, 免费使用2个月的 DigitalOcean 云主机(性价比超高, 每月只需5美元)
- 注册就送10美元, 免费使用4个月的 Vultr 云主机(性价比超高, 每月只需2.5美元)
- 注册就送10美元, 免费使用2个月的 阿里 云主机(性价比超高, 每月只需4.5美元)
- 注册就送20美元, 免费使用4个月的 Linode 云主机(性价比超高, 每月只需5美元) (折扣码: PodCastInit2022)
- PlusNet 英国光纤(超快, 超划算! 用户名 doctorlai)
- 刷了美国运通信用卡一年得到的积分 换了 485英镑
- 注册就送50英镑 – 英国最便宜最划算的电气提供商
- 能把比特币莱特币变现的银行卡! 不需要手续费就可以把虚拟货币法币兑换
微信公众号: 小赖子的英国生活和资讯 JustYYUK