EmguCV学习(二)

(1)图像锐化处理

//此部分代码功能是锐化图像,本质上是 扫描图像并访问相邻像素
//sharpened_pixel=5*current-left-right-up-down
byte saturateCast(double inValue)
{
    if (inValue < 0)
        return 0;
    else if (inValue > 255)
        return 255;
    else
        return (byte)inValue;
}
bool sharpen(Mat inImg,Mat outImg)
{
    int nchannel = inImg.NumberOfChannels;
    int nrows = inImg.Rows;
    int ncols = inImg.Cols;
    if(nchannel==1)
    {
        var tempImg=inImg.ToImage<Gray,byte>();
        var tempImg2 = tempImg.Clone();
        for(int i=1;i<nrows-1;++i)
        {
            for(int j=1;j<ncols-1;++j)
            {
                tempImg2.Data[i, j, 0] = saturateCast(5 * tempImg.Data[i, j, 0] - tempImg.Data[i - 1, j, 0] - tempImg.Data[i, j - 1, 0] -
                tempImg2.Data[i + 1, j, 0] - tempImg.Data[i, j + 1, 0]);
            }
        }
        for(int i=0;i<nrows;++i)
        {
            tempImg2.Data[i, 0, 0] = 0;
            tempImg2.Data[i, ncols - 1, 0] = 0;
        }
        for(int j=0;j<ncols;++j)
        {
            tempImg2.Data[0, j, 0] = 0;
            tempImg2.Data[nrows - 1, j, 0] = 0;
        }
        tempImg2.Mat.CopyTo(outImg);
    }
    else if(nchannel==3)
    {
        var tempImg = inImg.ToImage<Bgr, byte>();
        var tempImg2 = tempImg.Clone();
        for(int i=1;i<nrows-1;++i)
        {
            for(int j=1;j<ncols-1;++j)
            {
                for(int channel=0;channel<3;channel++)
                {
                    tempImg2.Data[i, j, channel] = saturateCast(5 * tempImg.Data[i, j, channel] - tempImg.Data[i - 1, j, channel] - tempImg.Data[i, j - 1, channel] -
                        tempImg.Data[i + 1, j, channel] - tempImg.Data[i, j + 1, channel]);
                }
            }
        }
        for(int i=0;i<nrows;++i)
        {
            for(int channel=0;channel<3;channel++)
            {
                tempImg2.Data[i, 0, channel] = 0;
                tempImg2.Data[i, ncols - 1, channel] = 0;
            }
        }
        for(int j=0;j<ncols;++j)
        {
            for(int channel=0;channel<3;channel++)
            {
                tempImg2.Data[0, j, channel] = 0;
                tempImg2.Data[nrows - 1, j, channel] = 0;
            }
        }
        tempImg2.Mat.CopyTo(outImg);
    }else
    {
        return false;
    }
    return true;
}
private void button27_Click(object sender, EventArgs e)
{
    Mat inImage = new Mat();
    Mat outImage = new Mat();
    if (chapter1OFD.ShowDialog() == DialogResult.OK)
        inImage = CvInvoke.Imread(chapter1OFD.FileName, LoadImageType.AnyColor | LoadImageType.AnyDepth);
    if (inImage.IsEmpty)
        return;
    if (!sharpen(inImage, outImage))
        return;
    if (outImage.IsEmpty)
        return;
    imageBox7.Image = outImage;
}
private void button28_Click(object sender, EventArgs e)
{
    if (chapter2Img.IsEmpty)
        return;
    Image<Gray, float> kernelI = new Image<Gray, float>(3, 3, new Gray(0));
    kernelI.Data[1, 1, 0] = (float)5.0;
    kernelI.Data[0, 1, 0] = (float)-1.0;
    kernelI.Data[1, 0, 0] = (float)-1.0;
    kernelI.Data[2, 1, 0] = (float)-1.0;
    kernelI.Data[1, 2, 0] = (float)-1.0;
    if(chapter2Img.NumberOfChannels==3)
    {
        var tempImg = chapter2Img.ToImage<Bgr, byte>();
        Image<Gray,byte>[] results = tempImg.Split();
        CvInvoke.Filter2D(results[0].Mat, results[0].Mat, kernelI.Mat, new Point(-1, -1));
        CvInvoke.Filter2D(results[1].Mat, results[1].Mat, kernelI.Mat, new Point(-1, -1));
        CvInvoke.Filter2D(results[2].Mat, results[2].Mat, kernelI.Mat, new Point(-1, -1));
                
        for(int i=0;i<tempImg.Rows;i++)
            for(int j=0;j<tempImg.Cols;j++)
            {
                tempImg.Data[i, j, 0] = results[0].Data[i, j, 0];
                tempImg.Data[i, j, 1] = results[1].Data[i, j, 0];
                tempImg.Data[i, j, 2] = results[2].Data[i, j, 0];
            }
        imageBox7.Image = tempImg;
    }
    else if(chapter2Img.NumberOfChannels==1)
    {
        var tempImg = chapter2Img.ToImage<Gray, byte>();
        CvInvoke.Filter2D(tempImg.Mat, tempImg.Mat, kernelI.Mat, new Point(-1, -1));
        imageBox7.Image = tempImg;
    }
}

以上两部分按钮,一个用的是自定义的函数进行锐化处理,一个用到了filter2d函数

214514_6Oiz_1460926.jpg

214529_d2pQ_1460926.jpg

(2)检测图片中的颜色(本例中为浅蓝色),并显示检测结果,监测结果为二值图像,白色表示所检测的颜色部分,黑色表示非所检测颜色部分

Mat chapter3Img = new Mat();
private void button29_Click(object sender, EventArgs e)
{
    if(chapter1OFD.ShowDialog()==DialogResult.OK)
    {
        chapter3Img = CvInvoke.Imread(chapter1OFD.FileName, LoadImageType.AnyColor | LoadImageType.AnyDepth);
        if (chapter3Img.IsEmpty)
            return;
    }
    imageBox11.Image = chapter3Img;
}
private void button30_Click(object sender, EventArgs e)
{
    if (chapter3Img.IsEmpty)
        return;
    ColorDetector cd = new ColorDetector();
    cd.setTargetColor(230, 190, 130);
    var result = cd.process(chapter3Img);
    CvInvoke.NamedWindow("result", NamedWindowType.Normal);
    CvInvoke.Imshow("result", result);
}
        

}
public class ColorDetector
{
    private int maxDist;
    private MCvScalar targetColor;
    private Mat result=new Mat();
    private void setTarget(int b,int g,int r)
{

    targetColor.V0 = b;
    targetColor.V1 = g;
    targetColor.V2 = r;
}
public void setTargetColor(byte blue,byte green,byte red)
{
    setTarget(blue, green, red);
}
public void setTargetColor(Color c)
{
    setTarget(c.B, c.G, c.R);
}
int getColorDistance(Color c1,Color c2)
{
    return (int)Math.Sqrt(Math.Pow((c1.B - c2.B), 2) + Math.Pow((c1.G - c2.G), 2) + Math.Pow((c1.R - c2.R), 2));
}
int getDistanceToTargetColor(Color c)
{
    Color tempC;
    tempC = Color.FromArgb((int)targetColor.V2, (int)targetColor.V1, (int)targetColor.V0);
    var ds=getColorDistance(c, tempC);
    以下是采用opencv的norm函数进行计算,仅仅为了说明一下,故将其注释掉
    //Mat temp = new Mat(3, 1, DepthType.Cv64F, 1);
    //var tempImg = temp.ToImage<Gray,float>();
    //tempImg.Data[0, 0, 0] = c.R - tempC.R;
    //tempImg.Data[1, 0, 0] = c.G - tempC.G;
    //tempImg.Data[2, 0, 0] = c.B - tempC.B;
    //var opencvDs = CvInvoke.Norm(tempImg);
    over
    return (int)ds;
}
public ColorDetector()
{
    maxDist = 100;
    setTarget(0, 0, 0);
}
void setColorDistanceThreshold(int distance)
{
    if (distance < 0)
        distance = 0;
    maxDist = distance;
}
int getColorDistanceThreshold()
{
    return maxDist;
}
public Mat process(Mat inImg)
{
    result.Create(inImg.Rows, inImg.Cols, DepthType.Cv8U, 1);
    var tempImg = result.ToImage<Gray, byte>();
    var tempImg2 = inImg.ToImage<Bgr, byte>();
    for(int i=0;i<tempImg2.Rows;i++)
        for(int j=0;j<tempImg2.Cols;j++)
        {
            if(getDistanceToTargetColor(Color.FromArgb(tempImg2.Data[i,j,2], tempImg2.Data[i, j, 1], tempImg2.Data[i, j, 0]))<maxDist)
            {
                tempImg.Data[i, j, 0] = 255;
            }
            else
            {
                tempImg.Data[i, j, 0] = 0;
            }
        }
    tempImg.Mat.CopyTo(result);
    return result;
}


214636_zU0i_1460926.jpg

(3)HSV彩色空间,以及基于色调和饱和度的皮肤检测

private void button31_Click(object sender, EventArgs e)//HSV分离
{
    if (chapter3Img.IsEmpty)
        return;
    var tempImg = new Mat();
    CvInvoke.CvtColor(chapter3Img, tempImg, ColorConversion.Bgr2Hsv);
    if (tempImg.IsEmpty)
        return;
    var imgs = tempImg.Split();
    imageBox12.Image = imgs[2];
    imageBox14.Image = imgs[1];
    imageBox13.Image = imgs[0];
}

private void button32_Click(object sender, EventArgs e)
{
    if (chapter3Img.IsEmpty)
        return;
    var tempImg = new Mat();
    CvInvoke.CvtColor(chapter3Img, tempImg, ColorConversion.Bgr2Hsv);
    if (tempImg.IsEmpty)
        return;
    //var imgs = tempImg.Split();
    //imgs[2].SetTo(new MCvScalar(255));
    var img = tempImg.ToImage<Hsv, byte>();
    for(int i=0;i<img.Rows;i++)
        for(int j=0;j<img.Cols;j++)
        {
            img.Data[i, j, 2] = 255;//统一亮度
        }
    CvInvoke.CvtColor(img.Mat, tempImg, ColorConversion.Hsv2Bgr);
    imageBox15.Image = tempImg;
}
//基于HSV彩色空间的皮肤识别
void detectHScolor(Mat inImg,double minHue,double maxHue,double minSat,double maxSat,out Mat mask)
{
    Mat HSVImg=new Mat();
    CvInvoke.CvtColor(inImg, HSVImg, ColorConversion.Bgr2Hsv);
    var hsvImgs = HSVImg.ToImage<Hsv, byte>().Split();
    //色调掩码
    Mat mask1 = new Mat();//小于maxHue
    CvInvoke.Threshold(hsvImgs[0], mask1, maxHue, 255,ThresholdType.BinaryInv);
    Mat mask2 = new Mat();//大于minHue
    CvInvoke.Threshold(hsvImgs[0], mask2, minHue, 255, ThresholdType.Binary);
    Mat hueMask = new Mat();//色调掩码
    if (minHue < maxHue)
        CvInvoke.BitwiseAnd(mask1, mask2, hueMask);
    else
        CvInvoke.BitwiseOr(mask1, mask2, hueMask);
    //饱和度掩码
    CvInvoke.Threshold(hsvImgs[1], mask1, maxSat, 255, ThresholdType.BinaryInv);
    CvInvoke.Threshold(hsvImgs[1], mask2, minSat, 255, ThresholdType.Binary);
    Mat satMask = new Mat();//饱和度掩码
    CvInvoke.BitwiseAnd(mask1, mask2, satMask);
    mask = new Mat();
    CvInvoke.BitwiseAnd(hueMask, satMask, mask);
    CvInvoke.NamedWindow("hue", NamedWindowType.Normal);
    CvInvoke.NamedWindow("sat", NamedWindowType.Normal);
    CvInvoke.Imshow("hue", hueMask);
    CvInvoke.Imshow("sat", satMask);
}
private void button33_Click(object sender, EventArgs e)
{
    if (chapter3Img.IsEmpty)
        return;
    Mat resultImg = new Mat();
    detectHScolor(chapter3Img, 160, 10, 25, 166, out resultImg);
    Mat detectedImg = chapter3Img.Clone();
    detectedImg.SetTo(new MCvScalar(0, 0, 0));
    chapter3Img.CopyTo(detectedImg, resultImg);
    imageBox16.Image = detectedImg;
}

152758_LQ2y_1460926.jpg

(4)直方图显示(灰度图像)

不得不吐槽下,我是准备用

public static void CalcHist(IInputArray images, int[] channels, IInputArray mask, IOutputArray hist, int[] histSize, float[] ranges, bool accumulate);

这个函数来进行计算的,但不知道为何,运行的时候总是会提示错误,捣鼓半天也查不到解决方案。然后,发现了Emgucv3的一个叫做DenseHistogram的类,该类主要是用来生成1D的直方图数据,颇为好用,故此部分用该类进行相关计算。在进行显示时,我用到了两种方法:使用HistogramViewer和使用HistogramBox控件。以下为代码:

Mat chapter4Img = new Mat();
private void button34_Click(object sender, EventArgs e)
{
    if (chapter1OFD.ShowDialog() == DialogResult.OK)
        chapter4Img = CvInvoke.Imread(chapter1OFD.FileName, LoadImageType.AnyColor | LoadImageType.AnyDepth);
    if (chapter4Img.IsEmpty)
        return;
    imageBox17.Image = chapter4Img;
}
//以下是直方图计算类(灰度图像),内部使用DenseHistogram类
class HisttoGram1D
{
    private int[] channels=new int[1];//使用的通道index
    private int[] histSize= new int[1];//bin数量
    private RangeF[] ranges=new RangeF[1];//像素值范围
    private Mat mask=new Mat();//掩码
    private Mat hist=new Mat();//得到的直方图数据
    public HisttoGram1D()
    {
        channels[0] = 0;
        histSize[0] = 256;
        ranges[0] = new RangeF(0.0f, 255.1f);
    }
    public DenseHistogram getDenseHistogram(Mat inImg)
    {
        if (inImg.IsEmpty)
        {
            return null;
        }
        DenseHistogram dh = new DenseHistogram(histSize, ranges);
        var imgs = inImg.ToImage<Gray, byte>().Split();
        dh.Calculate(imgs, false, null);//计算直方图信息
        return dh;
    }
}
//直方图类定义over
private void button35_Click(object sender, EventArgs e)//单通道图像
{
    Mat tempImg = new Mat();
    HisttoGram1D ht1D = new HisttoGram1D();
    if (chapter4Img.NumberOfChannels != 1)
    {
        CvInvoke.CvtColor(chapter4Img, tempImg, ColorConversion.Bgr2Gray);//彩色转灰度
    }
    else
    {
        tempImg = chapter4Img.Clone();
    }
    if (tempImg.IsEmpty)
        return;
    imageBox17.Image = tempImg;
    //以下采用HistogramViewer类直接显示图像直方图
    HistogramViewer hv = new HistogramViewer();
    hv.Text = "单通道直方图";
    hv.ShowInTaskbar = true;
    hv.HistogramCtrl.GenerateHistograms(tempImg, 256);
    hv.WindowState = FormWindowState.Normal;
    hv.Show();
    hv.Refresh();
    //还有一种方式,直接使用HistogramViewer的静态函数,HistogramViewer.show(tempImg,256);也可以
            
    //以下使用HistogramBox控件进行直方图显示,需要配合DenseHistogram类
    var dhData = ht1D.getDenseHistogram(tempImg);//获取DenseHistogram对象
    //值得注意的是,此时dhData,也就是DenseHistogram类,虽然是继承的Mat类,但里面存储的已经是直方图信息
    histogramBox2.ClearHistogram();
    histogramBox2.AddHistogram("HistogramBox控件显示单通道直方图", Color.FromArgb(255, 0, 0), dhData, 256, new float[] { 0.0f, 255.0f });
    histogramBox2.Refresh();

    int[] testt = new int[256];
    var ttt = tempImg.ToImage<Gray, byte>();
    for(int i=0;i<tempImg.Rows;i++)
        for(int j=0;j<tempImg.Cols;j++)
        {
            testt[ttt.Data[i, j, 0]]++;
        }
    int ii = 0;
    for(int i=0;i<testt.Length;i++)
    {
        ii += testt[i];
    }
    double iii = 0.0;
    for(int i=0;i<dhData.GetBinValues().Length;i++)
    {
        iii += dhData.GetBinValues()[i];
    }
    int iiii = 0;
}

注意,以上有一句:

ranges[0] = new RangeF(0.0f, 255.1f);

如果将RangeF的范围设为0-255,最后得到的直方图会缺少像素值为255点的统计信息。原因我猜是rangeF的范围取值是[ min,max )方式吧。

这个问题把我困了n久。。。囧

153940_uvdx_1460926.jpg

接下来的小节我将重写一个直方图类,借助DenseHistogram,实现探测图像的通道(暂定为单通道和RGB彩色图像),并显示直方图。

(5)这一部分,可以读取无论是RGB三通道图像还是Gray单通道图像,并生成直方图的Mat数据并显示。

另外,还完成了点击“计算RGB直方图”这一按钮功能。

//定义函数,输入Mat图像,输出DenseHistogram对象
DenseHistogram[] getDenseHistogramDataOfImage(Mat inImg)//这里的inImg深度暂定为byte
{

    DenseHistogram[] dh = new DenseHistogram[3] { new DenseHistogram(256, new RangeF(0f, 255.1f)), new DenseHistogram(256, new RangeF(0f, 255.1f)), new DenseHistogram(256, new RangeF(0f, 255.1f)) };
    if (inImg.NumberOfChannels==3)
    {
        dh[0].Calculate(new Image<Gray, byte>[] { inImg.ToImage<Bgr, byte>()[0] }, false, null);
        dh[1].Calculate(new Image<Gray, byte>[] { inImg.ToImage<Bgr, byte>()[1] }, false, null);
        dh[2].Calculate(new Image<Gray, byte>[] { inImg.ToImage<Bgr, byte>()[2] }, false, null);
    }
    else
    {
        dh[0].Calculate(new Image<Gray, byte>[] { inImg.ToImage<Gray, byte>()[0] }, false, null);
        dh[1] = null;
        dh[2] = null;
    }
    return dh;
}
private void button36_Click(object sender, EventArgs e)
{
    if (chapter4Img.IsEmpty)
        return;
    var dhData = getDenseHistogramDataOfImage(chapter4Img);
    histogramBox2.ClearHistogram();
    if(chapter4Img.NumberOfChannels==3)
    {
        histogramBox2.AddHistogram("Blue直方图", Color.FromArgb(0, 0, 255), dhData[0], 256, new float[] { 0.0f, 255.0f });
        histogramBox2.AddHistogram("Green直方图", Color.FromArgb(0, 255, 0), dhData[1], 256, new float[] { 0.0f, 255.0f });
        histogramBox2.AddHistogram("Red直方图", Color.FromArgb(255, 0, 0), dhData[2], 256, new float[] { 0.0f, 255.0f });
    }
    else
    {
        histogramBox2.AddHistogram("直方图", Color.FromArgb(0, 0, 0), dhData[0], 256, new float[] { 0.0f, 255.0f });
    }
    histogramBox2.Refresh();
}
private Mat AddMultiHistograms(DenseHistogram[] dh)//此函数功能实现由输入的直方图数据生成Mat图像
{
    double maxValue = 0;
    double minValue = 0;
    if (dh[1] == null)//说明用于计算直方图的图像为单通道
    {
        var arrayData = dh[0].GetBinValues();
        maxValue = arrayData.Max();
        minValue = arrayData.Min();
        for (int i = 0; i < arrayData.Length; i++)
        {
            arrayData[i] = (int)((arrayData[i] - minValue) / (maxValue - minValue) * 400.0);
        }
        var hist = new Image<Gray, byte>(512, 512, new Gray(255));
        Point p1 = new Point();
        Point p2 = new Point();
        p1.X = 0;
        p1.Y = 512-(int)arrayData[0];
        for (int i = 1; i < arrayData.Length; i++)
        {
            p2.X = i*2;
            p2.Y = 512-(int)arrayData[i];
            CvInvoke.Line(hist, p1, p2, new MCvScalar(0),3);
            p1.X = p2.X;
            p1.Y = p2.Y;
        }
        return hist.Mat;
    }
    else
    {
        List<float[]> arrayData = new List<float[]> { dh[0].GetBinValues(), dh[1].GetBinValues(), dh[2].GetBinValues() };//颜色顺序BGR
        maxValue = Math.Max(Math.Max(dh[0].GetBinValues().Max(), dh[1].GetBinValues().Max()), dh[2].GetBinValues().Max());
        minValue=Math.Min(Math.Min(dh[0].GetBinValues().Min(), dh[1].GetBinValues().Min()), dh[2].GetBinValues().Min());
        for(int index=0;index<arrayData.Count;index++)
        {
            for(int i=0;i<arrayData[index].Length;i++)
            {
                arrayData[index][i] = (float)((arrayData[index][i] - minValue) / (maxValue - minValue)*400.0);
            }
        }
        var hist = new Image<Bgr, byte>(512, 512, new Bgr(255, 255, 255));//初始化图像
        Point p1 = new Point();
        Point p2 = new Point();
        var colors = new MCvScalar[3] { new MCvScalar(255, 0, 0), new MCvScalar(0, 255, 0), new MCvScalar(0, 0, 255) };
        for(int index=0;index<arrayData.Count;index++)
        {
            p1.X = 0;
            p1.Y = 512 - (int)arrayData[index][0];
            for(int i=1;i<arrayData[index].Length;i++)
            {
                p2.X = i*2;
                p2.Y = 512 - (int)arrayData[index][i];
                CvInvoke.Line(hist, p1, p2, colors[index], 3);
                p1.X = p2.X;
                p1.Y = p2.Y;
            }
        }
        return hist.Mat;
    }
}
private void button37_Click(object sender, EventArgs e)
{
    if (chapter4Img.IsEmpty)
        return;
    var dhData = getDenseHistogramDataOfImage(chapter4Img);
    var histMat = AddMultiHistograms(dhData);
    imageBox18.Image = histMat;
}

225006_AkKm_1460926.jpg

225006_15Qr_1460926.jpg

(6)基于查找表的颜色反转

无论是彩色还是单通道都可以哈

//利用查找表反转灰度图像
private void button38_Click(object sender, EventArgs e)
{
    if (chapter4Img.IsEmpty)
        return;
    Mat lut = new Mat(1, 256, DepthType.Cv8U, 1);
    byte[] data = new byte[256];
    for(int i=0;i<data.Length;i++)
    {
        data[i] = (byte)(256 - i);
    }
    lut.SetTo(data);
    Mat resultImg = new Mat();
    CvInvoke.LUT(chapter4Img, lut, resultImg);
    imageBox17.Image = resultImg;
}

113357_zloE_1460926.jpg



转载于:https://my.oschina.net/u/1460926/blog/648017

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值