图像二值化

黑色(0)表示背景

白色(1-255)表示对象

Threshold二值化

//函数原型
double Threshold(InputArray src, 
    OutputArray dst, 
    double thresh, 
    double maxval, 
    ThresholdTypes type)



Threshold图像二值化

函数返回值:返回二值化的thresh,type为Ostu与Triangle时,计算得到,其它时与参数thresh一致

参数

说明

InputArray src

输入图像:8位或32位浮点型多通道(使用OTSU或TRIANGLE时,只能8位单通道)

OutputArray dst

输出图像:与输入图像相同的大小、类型和通道数

double thresh

阈值:type为Otsu或Triangle时,此参数忽略

double maxval

最大值

ThresholdTypes type

二值化类型:Ostu与Triangle可以其它组合使用



参数ThresholdTypes说明

取值

说明

Binary

src(x,y)>thresh时为maxval;否则为0

BinaryInv

src(x,y)>thresh时为0;否则为maxval

Trunc

src(x,y)>thresh时为thresh;否则为src(x,y)

Tozero

src(x,y)>thresh时为0;否则为src(x,y)

TozeroInv

src(x,y)>thresh时为src(x,y);否则为0

Otsu

使用大津法计算thresh,再二值化

Triangle

使用三角法计算thresh,再二值化



各种ThresholdType结果示例




opencv 文本二值处理 opencvsharp 二值化_opencv 文本二值处理


AdaptiveThreshold自适应二值化

//函数原型
void AdaptiveThreshold(InputArray src, 
    OutputArray dst, 
    double maxValue, 
    AdaptiveThresholdTypes adaptiveMethod, 
    ThresholdTypes thresholdType, 
    int blockSize, 
    double c)


AdaptiveThreshold自适应阈值

参数

说明

InputArray src

输入图像:8位单通道

OutputArray dst

输出图像:与输入图像相同的大小、类型和通道数

double maxValue

最大值:二值化后的最大值

AdaptiveThresholdTypes adaptiveMethod

适应方式

ThresholdTypes thresholdType

阈值类型:THRESH_BINARY 或 THRESH_BINARY_INV

int blockSize

块大小:大于等于3的奇数(经验值25?)

double c

C常量:用于平均值或加权平均值减去值,正负0均可(经验值10?)


opencv 文本二值处理 opencvsharp 二值化_Powered by 金山文档_02


源码示例

public void Run()
{
    using var src = Cv2.ImRead(ImagePath.LenaColor, ImreadModes.Grayscale);
    if (src.Empty()) throw new Exception("图像读取有误");

    using var dst = new Mat();
    double thresh = 127;
    double maxVal = 255;
    Cv2.Threshold(src, dst, thresh, maxVal, ThresholdTypes.Binary);
    Cv2.ImShow($"Threshold-Binary :{thresh}", dst);

    //计算图像的均值
    thresh = Cv2.Mean(src).ToDouble();
    Cv2.Threshold(src, dst, thresh, maxVal, ThresholdTypes.Binary);
    Cv2.ImShow($"Threshold-Binary:{thresh}", dst);

    //三角法:对直方图有一个峰的处理结果较优;第三个参数,不影响结果
    thresh = Cv2.Threshold(src, dst, 0, maxVal, ThresholdTypes.Triangle | ThresholdTypes.Binary);
    Cv2.ImShow($"Threshold-Triangle|Binary: {thresh}", dst);

    //大津法:对直方图有两个峰的处理结果较优;第三个参数,不影响结果
    thresh = Cv2.Threshold(src, dst, 0, maxVal, ThresholdTypes.Otsu | ThresholdTypes.Binary);
    Cv2.ImShow($"Threshold-Ostu|Binary: {thresh}", dst);

    Cv2.Threshold(src, dst, thresh, maxVal, ThresholdTypes.BinaryInv);
    Cv2.ImShow($"Threshold BinaryInv: {thresh}", dst);

    Cv2.Threshold(src, dst, thresh, maxVal, ThresholdTypes.Trunc);
    Cv2.ImShow($"Threshold Trunc: {thresh}", dst);

    Cv2.Threshold(src, dst, thresh, maxVal, ThresholdTypes.Tozero);
    Cv2.ImShow($"Threshold Tozero: {thresh}", dst);

    Cv2.Threshold(src, dst, thresh, maxVal, ThresholdTypes.TozeroInv);
    Cv2.ImShow($"Threshold TozeroInv: {thresh}", dst);

    var wName = "AdaptiveThreshold Demo";
    Cv2.NamedWindow(wName, WindowFlags.AutoSize);

    var tbBlockSize = "blockSize";
    var tbC = "C-255";
    var tbMaxValue = "maxValue";

    int blockSize = 25;
    int adaptiveC = 10 + 255;
    int maxvalue = 255;
    Cv2.CreateTrackbar(tbBlockSize, wName, ref blockSize, 255, tbOnChanged);
    Cv2.CreateTrackbar(tbC, wName, ref adaptiveC, 255 * 2, tbOnChanged);
    Cv2.CreateTrackbar(tbMaxValue, wName, ref maxvalue, 255, tbOnChanged);

    while (true)
    {
        if (Cv2.WaitKey(1) == (int)Keys.Escape) break;
        if (tbChanged)
        {
            if (blockSize % 2 == 0)//必须为正奇数
            {
                Cv2.SetTrackbarPos(tbBlockSize, wName, blockSize + 1);
                continue;
            }
            if (blockSize == 1) continue;//不能为1
            //GaussianC,
            Cv2.AdaptiveThreshold(src, dst, maxvalue, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, blockSize, adaptiveC - 255);
            dst.PutText("GaussianC", new Point(30, 50), HersheyFonts.HersheySimplex, 0.8, Scalar.Black);
            Cv2.Line(dst, new Point(dst.Width - 1, 0), new Point(dst.Width - 1, dst.Height), Scalar.Black);

            //MeanC
            using var dst1 = new Mat();
            Cv2.AdaptiveThreshold(src, dst1, maxvalue, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, blockSize, adaptiveC - 255);
            dst1.PutText("MeanC", new Point(30, 50), HersheyFonts.HersheySimplex, 0.8, Scalar.Black);
            Cv2.HConcat(new[] { dst, dst1 }, dst);
            
            Cv2.ImShow(wName, dst);
            tbChanged = false;
        }
    }
    DrawThresholdTypesDemo();
    Cv2.WaitKey();
    Cv2.DestroyAllWindows();
}
private bool tbChanged = false;
private void tbOnChanged(int pos,IntPtr userData)
{
    tbChanged = true;
}


//二值化示例窗口名
private string winName = "ThresholdTypes Demo";
//当前阈值
private int thresh = 200;
//最大值
private int maxVal = 255;

/// <summary>
/// 生成各种二值化图例
/// </summary>
private void DrawThresholdTypesDemo()
{
    
    using (var srcMat = new Mat(1, 255, MatType.CV_8U, new Scalar(0)))
    {
        //初始待二值化图像
        for (int i = 0; i < 50; i++)
        {
            srcMat.At<byte>(0, i) = (byte)(i * 2 + 150);
        }
        for (int i = 50; i < 170; i++)
        {
            srcMat.At<byte>(0, i) = (byte)(350 - i * 2);
        }
        for (int i = 170; i < 256; i++)
        {
            srcMat.At<byte>(0, i) = (byte)(i * 2.5 - 412);
        }
        Cv2.NamedWindow(winName, WindowFlags.AutoSize);
        Cv2.CreateTrackbar("thresh", winName, ref thresh, 255, OnChanged);
        Cv2.CreateTrackbar("maxVal", winName, ref maxVal, 255, OnChanged);

        while (true)
        {
            if (Cv2.WaitKey(50) == (int)Keys.Escape) break;
            if(posChanged) DrawDemo(srcMat);
        }
        Cv2.DestroyAllWindows();                
    }
}
/// <summary>
/// 示例各种二值化结果
/// </summary>
/// <param name="srcMat"></param>
private void DrawDemo(Mat srcMat)
{
    //不重新获取的话,滚动一会后会失效?
    thresh = Cv2.GetTrackbarPos("thresh", winName);
    maxVal = Cv2.GetTrackbarPos("maxVal", winName);
    using var thresholdMat = new Mat();

    int graphIndex = 0;//行高偏移系数

    var resultImg = new Mat(900, 1530, MatType.CV_8UC3, new Scalar(255, 255, 255));
    //待二值化数据
    DrawLine(resultImg, srcMat, graphIndex, thresh, maxVal, $"Origin,thresh={thresh}");

    graphIndex++;
    Cv2.Threshold(srcMat, thresholdMat, thresh, maxVal, ThresholdTypes.Binary);
    DrawLine(resultImg, thresholdMat, graphIndex, thresh, maxVal, $"Binary,thresh={thresh}");

    graphIndex++;
    Cv2.Threshold(srcMat, thresholdMat, thresh, maxVal, ThresholdTypes.BinaryInv);
    DrawLine(resultImg, thresholdMat, graphIndex, thresh, maxVal, $"BinaryInv,thresh={thresh}");

    graphIndex++;
    Cv2.Threshold(srcMat, thresholdMat, thresh, maxVal, ThresholdTypes.Trunc);
    DrawLine(resultImg, thresholdMat, graphIndex, thresh, maxVal, $"Trunc,thresh={thresh}");

    graphIndex++;
    Cv2.Threshold(srcMat, thresholdMat, thresh, maxVal, ThresholdTypes.TozeroInv);
    DrawLine(resultImg, thresholdMat, graphIndex, thresh, maxVal, $"TozeroInv,thresh={thresh}");

    graphIndex++;
    Cv2.Threshold(srcMat, thresholdMat, thresh, maxVal, ThresholdTypes.Tozero);
    DrawLine(resultImg, thresholdMat, graphIndex, thresh, maxVal, $"Tozero,thresh={thresh}");

    graphIndex++;
    var calcThresh = Cv2.Threshold(srcMat, thresholdMat, thresh, maxVal, ThresholdTypes.Otsu | ThresholdTypes.Binary);
    DrawLine(resultImg, thresholdMat, graphIndex, calcThresh, maxVal, $"Otsu,Binary,thresh={calcThresh}");

    graphIndex++;
    calcThresh = Cv2.Threshold(srcMat, thresholdMat, thresh, maxVal, ThresholdTypes.Triangle | ThresholdTypes.BinaryInv);
    DrawLine(resultImg, thresholdMat, graphIndex, calcThresh, maxVal, $"Triangle,BinaryInv,thresh={calcThresh}");

    Cv2.ImShow(winName, resultImg);
    posChanged = false;
}

/// <summary>
/// 绘制最大值、阈值及二值化结果
/// </summary>
/// <param name="resultImg">绘制结果</param>
/// <param name="thresholdMat">二值化图像</param>
/// <param name="graphIndex">最几个图例,用于控制绘图位置</param>
/// <param name="thresh">阈值</param>
/// <param name="maxVal">最大值</param>
/// <param name="text">提示文字</param>
private void DrawLine(Mat resultImg, Mat thresholdMat, int graphIndex, double thresh, double maxVal, string text)
{
    var graphWidth = resultImg.Width / 2;//分左右两列
    var graphHeight = 220;

    var binW = Math.Round((double)1D * (graphWidth - 100) / thresholdMat.Width, 2);
    var offsetX = (graphWidth - thresholdMat.Width * binW) / 2 + graphWidth * (graphIndex % 2);
    var binH = 0.5;
    var offsetY = graphIndex / 2 * graphHeight + 50;
    var thickness = 2;
    for (int i = 1; i < thresholdMat.Width; i++)
    {
        var pt1 = new Point2d(binW * (i - 1) + offsetX, binH * (255 - thresholdMat.At<byte>(0, i - 1)) + offsetY);
        var pt2 = new Point2d(binW * i + offsetX, binH * (255 - thresholdMat.At<byte>(0, i)) + offsetY);
        //二值线,黑色
        Cv2.Line(resultImg, (Point)pt1, (Point)pt2, Scalar.Black, thickness, LineTypes.AntiAlias);

        //分隔线
        if ((i - 1) % 25 == 0)
        {
            Cv2.Line(resultImg, new Point(binW * i + offsetX, offsetY), new Point(binW * i + offsetX, binH * 255 + offsetY), Scalar.LightGray);
        }
    }
    var x1 = offsetX;
    var x2 = offsetX + thresholdMat.Width * binW;
    //thresh阈值线,绿色
    Cv2.Line(resultImg, new Point(x1, binH * (255 - thresh) + offsetY),
                        new Point(x2, binH * (255 - thresh) + offsetY), Scalar.Green, 1);
    //maxVal最大阈值线,红色
    Cv2.Line(resultImg, new Point(x1, binH * (255 - maxVal) + offsetY),
                        new Point(x2, binH * (255 - maxVal) + offsetY), Scalar.Red, 1);
    //0值,蓝色
    Cv2.Line(resultImg, new Point(x1, binH * (255 - 0) + offsetY),
                        new Point(x2, binH * (255 - 0) + offsetY), Scalar.Blue, 1);

    Cv2.PutText(resultImg, text+ ",Red:maxval,Green:thresh,Blue:0,Black:result", new Point(offsetX, binH * 280 + offsetY), HersheyFonts.HersheySimplex, 0.5, Scalar.Black);
}

//滚动条是否变过
private bool posChanged = false;
private void OnChanged(int pos,IntPtr userData)
{
    posChanged = true;
}

OpenCvSharp函数示例目录