移动平均的阈值处理 opencv实现


本系列文章由@邻居张师傅 出品,转载请注明出处。

文章链接: https://editor.csdn.net/md?articleId=106491750

邮箱: zhangyh_nb@163.com

当前使用OpenCV版本: 4.0.1



原理

相比于对整幅图像取一个固定的阈值,由于光照不均匀等原因,跟一般的方法是对一幅图像中的每个像素点计算阈值,即可变局部阈值。
算法表示如下:
m ( k + 1 ) = 1 n ∑ i = k + 2 − n k + 1 z i ( 1.1 ) m(k+1)=\frac{1}{n}\sum^{k+1}_{i=k+2-n}z_{i}(1.1) m(k+1)=n1i=k+2nk+1zi1.1
其中, z k + 1 z_{k+1} zk+1表示在扫描序列中第 k + 1 k+1 k+1步遇到的点的灰度值, m ( k ) m(k) m(k)为输入图像的第 k k k个点的像素值, n n n表示用于计算平均的点的数量。
根据(1.1)有下式:
m ( k ) = 1 n ∑ i = k + 1 − n k z i ( 1.2 ) m(k)=\frac{1}{n}\sum^{k}_{i=k+1-n}z_{i}(1.2) m(k)=n1i=k+1nkzi1.2
根据(1.1)、(1.2)不难得出下式子:
m ( k + 1 ) = 1 n ∑ i = k + 2 − n k + 1 z i = m ( k ) + 1 n ( z k + 1 − z k + 1 − n ) ( 1.3 ) m(k+1)=\frac{1}{n}\sum^{k+1}_{i=k+2-n}z_{i}=m(k)+\frac{1}{n}(z_{k+1}-z_{k+1-n})(1.3) m(k+1)=n1i=k+2nk+1zi=m(k)+n1(zk+1zk+1n)1.3
接下来就根据每个点的阈值进行二值化操作。
注:我们仅仅在扫描的点的数量大于n时应用式(1.3),其他情况则将 z k + 1 − n z_{k+1-n} zk+1n视为0,即相当于在图像的边界填充了n-1个0


实现

以下是实现结果,左边是原图,右边是结果。
在这里插入图片描述下面是代码:

#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;


static void ThresholdOp (int, void*);
void movingThreshold(const Mat &src,Mat &dst,int n=20,float b=0.5);

int source=0,thresholdType=0;
Mat src,dst;
int main(int argc, char* argv[])
{

	namedWindow( "ThresholdOp", WINDOW_NORMAL );
    createTrackbar( "Source:", "ThresholdOp", &source, 1, ThresholdOp );
    createTrackbar( "Type:", "ThresholdOp", &thresholdType, 1, ThresholdOp );

    ThresholdOp(0, 0);
	waitKey(0);

	return 0;
}

static void ThresholdOp (int, void*){
    switch(source){
    case 0:
    src = imread("1.tif", IMREAD_GRAYSCALE);
    namedWindow( "1.tif", WINDOW_NORMAL );
    imshow("1.tif", src);
    break;

    case 1:
    src = imread("2.tif", IMREAD_GRAYSCALE);
    namedWindow( "2.tif", WINDOW_NORMAL );
    imshow("2.tif", src);
    break;
    }
    switch(thresholdType){
    case 0:
    threshold(src, dst, 0, 255, THRESH_OTSU);
    imshow("ThresholdOp", dst);
    break;

    case 1:
    movingThreshold(src,dst);
    imshow("ThresholdOp", dst);
    break;

    }
}

void movingThreshold(const Mat &src,Mat &dst,int n,float b){

cout<<n<<" "<<b;
    int arrayNum=src.rows*src.cols;
    uchar *temp = new uchar[arrayNum];
    for(int y=0;y<src.rows;y++){//二维转换为一维Z字扫描
        for(int x=0;x<src.cols;x++){
            if(y%2==0)
                temp[y*src.cols+x]=src.at<uchar>(y,x);
            else
                temp[y*src.cols+x]=src.at<uchar>(y,src.cols-1-x);
        }
    }

    dst.create(src.size(),CV_8U);
    float m_now=0,m_pre=0,dif=0;
    int index=0;
    for(int y=0;y<src.rows;y++)
        for(int x=0;x<src.cols;x++){
            index=y*src.cols+x;
            if(index<n)//当前点总数不足时
                dif=temp[index];
            else
                dif=temp[index]-temp[index-n];

            dif *= (float)1 / n;
            m_now=m_pre+dif;//得出阈值
            m_pre=m_now;
            if(src.at<uchar>(y,x)>b*m_now)
                dst.at<uchar>(y,x)=255;
            else
                dst.at<uchar>(y,x)=0;
    }
    delete [] temp;
}


参考

1.冈萨雷斯. 数字图像处理[M]. 北京: 电子工业出版社, 2005.

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是一个基于Qt和OpenCV的简单移动检测实现: ```cpp #include <QCoreApplication> #include <QObject> #include <QThread> #include <QDebug> #include <opencv2/opencv.hpp> using namespace cv; class VideoCaptureWorker : public QObject { Q_OBJECT public: VideoCaptureWorker(QObject *parent = nullptr) : QObject(parent) {} public slots: void doWork() { VideoCapture cap(0); if (!cap.isOpened()) { qDebug() << "Failed to open camera!"; return; } Mat prevFrame, currFrame; cap >> prevFrame; cv::cvtColor(prevFrame, prevFrame, COLOR_BGR2GRAY); while (true) { cap >> currFrame; cv::cvtColor(currFrame, currFrame, COLOR_BGR2GRAY); Mat diffFrame; cv::absdiff(currFrame, prevFrame, diffFrame); cv::threshold(diffFrame, diffFrame, 30, 255, THRESH_BINARY); int count = cv::countNonZero(diffFrame); qDebug() << "Count: " << count; if (count > 10000) { qDebug() << "Movement detected!"; } prevFrame = currFrame.clone(); cv::imshow("Camera", currFrame); if (waitKey(30) == 'q') { break; } } cap.release(); } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); VideoCaptureWorker *worker = new VideoCaptureWorker(&a); QThread *thread = new QThread(&a); QObject::connect(thread, &QThread::started, worker, &VideoCaptureWorker::doWork); QObject::connect(&a, &QCoreApplication::aboutToQuit, thread, &QThread::quit); worker->moveToThread(thread); thread->start(); return a.exec(); } ``` 这个例子中,我们使用了Qt的多线程机制,将摄像头读取和图像处理的工作放在了一个独立的线程中。在每一帧图像中,我们通过计算当前帧和前一帧的灰度图像的差异,得到一个二值图像。如果差异像素点的数量超过一定阈值,则判断为有移动发生。在控制台输出提示信息,并可以通过弹出窗口显示当前帧的图像。 需要注意的是,这只是一个简单的移动检测实现,实际使用中需要针对具体场景进行算法和参数的调整,以获得更好的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值