OpencvSharp 算子学习教案之 - Cv2.InRange 重载2
OpencvSharp 算子学习教案之 - Cv2.InRange 重载2
重载2:InRange(InputArray src, Scalar lowerb, Scalar upperb, OutputArray dst)
大家好,Opencv在很多工程项目中都会用到,而OpencvSharp则是以C#开发与实现的Opencv操作库,对.NET开发人员友好,但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳,因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案,供大家参考学习。
Cv2.InRange
- 教案版本:V1.0
- 面向对象:OpenCvSharp 初学者
- 所属模块:core
- 源码位置:OpenCvSharp/Cv2/Cv2_core.cs:1468
摘要:这个重载用两个 Scalar 作为固定边界,判断源数组是否落在闭区间内,输出仍然是 CV_8U 掩码。本文把它和矩阵边界重载分开讲,帮助初学者先掌握“固定阈值筛选”的最常用写法。
1. 函数名称(带参数签名)
public static void InRange(InputArray src, Scalar lowerb, Scalar upperb, OutputArray dst)
2. 函数用途
Cv2.InRange 的标量重载,适合用固定的下界和上界去筛选图像中的像素。
它最常见的用途有:
- 提取某个固定灰度范围。
- 作为颜色阈值筛选的基础。
- 生成二值掩码供后续逻辑运算使用。
- 作为初学者理解“阈值区间”的第一步。
和矩阵边界重载相比,这个版本更容易上手,因为下界和上界都是固定常量。
3. 函数公式
对任意像素位置 (x, y),这个重载可以写成:
dst(x,y)={255,lowerb≤src(x,y)≤upperb0,otherwise dst(x, y) = \begin{cases} 255, & lowerb \le src(x, y) \le upperb \\ 0, & \text{otherwise} \end{cases} dst(x,y)={255,0,lowerb≤src(x,y)≤upperbotherwise
这个公式说明:
- 下界和上界都包含在内。
- 不满足范围时直接写 0。
- 输出是二值掩码,不保留原始灰度。
4. 函数原理说明
InRange 标量重载的工作过程可以理解成下面三步:
- 读取源像素值。
- 用固定的下界和上界做比较。
- 把结果写成 255 或 0。
初学者最容易弄混的地方是:
- 以为下界或上界不包含在内,其实两端都包含。
- 以为结果还是原始像素值,其实结果只有 0 和 255。
- 以为它只能用于灰度图,其实彩色图也可以用,只是每个通道都要满足条件。
- 以为它和普通阈值化完全一样,其实它允许同时指定上下界。
本页使用四象限灰度图,演示固定阈值筛选的效果。
5. 参数含义解析
| 参数名 | 类型 | 必填 | 含义 |
|---|---|---|---|
| src | InputArray | 是 | 输入数组 |
| lowerb | Scalar | 是 | 下界标量 |
| upperb | Scalar | 是 | 上界标量 |
| dst | OutputArray | 是 | 输出的二值掩码 |
补充说明:
- 对灰度图像来说,Scalar 可以理解成一个固定的阈值常量。
- 对多通道图像来说,需要每个通道都满足范围条件。
- 输出图像的类型是
CV_8U。 - 这个写法是固定阈值筛选中最常见的一种。
6. 应用场景列表
| 场景名 | 场景说明 | 典型用途 |
|---|---|---|
| 场景A:固定灰度筛选 | 提取一个灰度区间 | 简单目标分割 |
| 场景B:颜色区间筛选 | 在颜色空间里提取某一类颜色 | 颜色分割、检测 |
| 场景C:二值掩码生成 | 生成 0/255 的判断图 | 后续形态学处理 |
| 场景D:阈值教学 | 直观理解上下界区间 | 入门教学、调试 |
7. 函数使用示例
下面的 Console 程序演示 Cv2.InRange 的 Scalar 重载。示例使用固定下界和上界去筛选四个灰度象限,能够很直观地看出哪些区域被保留下来了。
using System;
using System.Text;
using OpenCvSharp;
internal static class Program
{
/// <summary>
/// 程序入口。
/// </summary>
private static void Main()
{
// 让控制台正确显示中文。
Console.OutputEncoding = Encoding.UTF8;
RunScenario();
}
/// <summary>
/// 演示标量边界重载。
/// </summary>
private static void RunScenario()
{
// 源图像的四个象限分别放四个灰度值,便于观察哪些象限会落在固定区间里。
var sourceValues = new byte[,]
{
{ 50, 120 },
{ 170, 230 },
};
const byte lowerValue = 100;
const byte upperValue = 200;
using var source = CreateGrayMat(sourceValues);
using var destination = new Mat();
// 标量边界写法最适合固定阈值筛选。
Cv2.InRange(source, new Scalar(lowerValue), new Scalar(upperValue), destination);
var expected = InRangeExpected(sourceValues, lowerValue, upperValue);
PrintComparison($"InRange(InputArray src, Scalar lowerb, Scalar upperb, OutputArray dst)", destination, expected);
}
/// <summary>
/// 把二维灰度数组写入 Mat。
/// </summary>
private static Mat CreateGrayMat(byte[,] values)
{
var rows = values.GetLength(0);
var cols = values.GetLength(1);
var mat = new Mat(rows, cols, MatType.CV_8UC1);
for (var row = 0; row < rows; row++)
{
for (var col = 0; col < cols; col++)
{
// 逐像素写入,方便后面和 InRange 的结果做逐点对比。
mat.At<byte>(row, col) = values[row, col];
}
}
return mat;
}
/// <summary>
/// 按照闭区间规则计算期望结果。
/// </summary>
/// <param name="source">源矩阵。</param>
/// <param name="lower">下界。</param>
/// <param name="upper">上界。</param>
/// <returns>期望结果。</returns>
private static byte[,] InRangeExpected(byte[,] source, byte lower, byte upper)
{
var rows = source.GetLength(0);
var cols = source.GetLength(1);
var result = new byte[rows, cols];
for (var row = 0; row < rows; row++)
{
for (var col = 0; col < cols; col++)
{
var value = source[row, col];
result[row, col] = (byte)(value >= lower && value <= upper ? 255 : 0);
}
}
return result;
}
/// <summary>
/// 打印彩色矩阵并检查结果。
/// </summary>
private static void PrintComparison(string title, Mat actual, byte[,] expected)
{
var actualMatrix = ReadGrayMat(actual);
Console.WriteLine(title);
Console.WriteLine("实际结果:");
Console.WriteLine(FormatGrayMatrix(actualMatrix));
Console.WriteLine("期望结果:");
Console.WriteLine(FormatGrayMatrix(expected));
Console.WriteLine($"是否完全一致:{AreEqual(actualMatrix, expected)}");
Console.WriteLine();
}
/// <summary>
/// 把 Mat 读成灰度二维数组。
/// </summary>
private static byte[,] ReadGrayMat(Mat source)
{
var result = new byte[source.Rows, source.Cols];
for (var row = 0; row < source.Rows; row++)
{
for (var col = 0; col < source.Cols; col++)
{
result[row, col] = source.At<byte>(row, col);
}
}
return result;
}
/// <summary>
/// 格式化灰度矩阵。
/// </summary>
private static string FormatGrayMatrix(byte[,] matrix)
{
var sb = new StringBuilder();
for (var row = 0; row < matrix.GetLength(0); row++)
{
sb.Append("[");
for (var col = 0; col < matrix.GetLength(1); col++)
{
sb.Append(matrix[row, col]);
if (col < matrix.GetLength(1) - 1)
{
sb.Append(", ");
}
}
sb.AppendLine("]");
}
return sb.ToString();
}
/// <summary>
/// 判断两个灰度矩阵是否完全一致。
/// </summary>
private static bool AreEqual(byte[,] actual, byte[,] expected)
{
if (actual.GetLength(0) != expected.GetLength(0) || actual.GetLength(1) != expected.GetLength(1))
{
return false;
}
for (var row = 0; row < actual.GetLength(0); row++)
{
for (var col = 0; col < actual.GetLength(1); col++)
{
if (actual[row, col] != expected[row, col])
{
return false;
}
}
}
return true;
}
}
8. 注意事项
InRange返回的是 0/255 掩码,不是原始像素。- Scalar 重载最适合固定阈值筛选。
- 区间两端都是包含的,不要把它理解成开区间。
- 多通道图像时要每个通道都满足条件。
- 输出类型固定是
CV_8U。
9. 调优建议
- 初学者先用灰度图理解固定阈值筛选。
- 如果结果太少或太多,先调整 lowerb 和 upperb。
- 做颜色提取时,优先选择更适合阈值判断的颜色空间。
- 如果你只需要固定范围筛选,Scalar 重载最简单。
10. 运行说明
- 如果你在控制台工程里运行本文示例,直接把代码放进
Program.cs即可。 - 如果你在本仓库里学习,请直接打开 WPF 控件
Cv2.InRange,先看矩阵边界场景,再看标量边界场景。 - WPF 示例会把源图、下界、上界和结果图一起展示,方便对照理解。
11. 常见错误排查
- 把 InRange 误当成普通的二值化函数,却忘了它的闭区间语义。
- 忘记区间两端都包含。
- 把结果误认为原始灰度值。
- 让源图和阈值设置不匹配,导致结果过少或过多。
- 忽略多通道图像必须所有通道都满足条件。
更多推荐
所有评论(0)