OpenCV学习笔记(二)操作像素

1、访问像素值方法:直接访问、使用指针、使用迭代器。

2、二值图像中,0代表黑色,1代表白色;灰度图像(8位)0代表黑色,255代表白色。

3、直接访问
Mat类中at方法被实现为一个模板方法,因此调用at时必须指定图像元素类型,eg: image.at<uchar>(j,i) = 255;尖括号内内是模板的参数列表。需要注意指定的类型与矩阵类的元素类型一致,at方法不会进行任何类型转换。对于彩色图像,Mat提供一种向量,将这种短向量 定义为一种类型Vec3b,使用下标运算符依次访问向量中的三个元素,访问方法如下:
 

image.at<Vec3b>(j,i)[channel] = value;            //channel是通道序号0,1,2
image.at<Vec3b>(j,i) = Vec3b(255,255,255);

修改图像的函数使用图像作为参数,使用了值传递,是因为他们在复制图像的时候共享了图像头,不需要使用引用传递。同时通过这种访问方式就可以访问像素。

4、减色算法

void colotReduce(Mat image, int diw = 64);            //函数签名
void colotReduce(Mat image, int diw = 64)
{
    int ni = image.rows;                              //行数
    int nc = image.cols * image.channels();           //每行中的元素
    for(int j = 0; j < ni; j++)
    {
        uchar* data = image.ptr<uchar>(j);            ptr模板函数返回第j行的首元素地址
        for(int i = 0; i < nc; i++)
            {
                data[i] = data[i]/div * div + div/2;
                //*data++ = *data/div * div + div/2;    //这是用指针递增来遍历一行中的元素
            }
    }
        
}

减色算法的实现除了上述的整数除法性质,也可以用取模运算data[i] - data[i]%div + div/2;还可以使用位运算符 ,但是没看懂。。。。(位运算是效率最高的)

5、用指针访问

4中的减色函数直接在输入图像中进行了转换,称为就地转换,但有时希望保护原图像,可以考虑克隆一个副本,对副本操作,但可以构造一种方法,实现将这两种方法综合。使用下述函数,当选择就地转换时,可以让输入输出为同一图像,不用再函数外创建result对象,如果不就地转换,就需要创建而且输入输出不同。
 

void colorReduce(const Mat& image, Mat& result, int div = 64);
//如果采取就地处理,则image与result为同一值,否则在函数外new一个result

Mat result;
void colorReduce(const Mat& image, Mat& result, int div = 64);
{
    int ni = image.rows;                              //行数
    int nc = image.cols * image.channels();           //每行中的元素
    result.creat(image.rows, image.cols, image.type();
    for(int j = 0;j < ni; j++)
    {
        const uchar* data_in = image.ptr<uchar>(j);//全过程只使用输入图像并未改变图像
        uchar* data_out = result.ptr<uchar>(j);
        for(int i = 0;i < nc; i++)
        {
            data_out[i] = data_in[i]/div*div + div/2;
        }

    }
}

6、对连续图像的高速扫描

为了提高性能,会添加像素,成8的倍数。去掉填充后的图像可以看做是一个长一维数组,行与行之间是连续的,此时认为该图像具有连续性,我们可以用isContinuous方法检测图像是否有连续性,如有连续性,便可以将20×20的二维图像转换为1×40的 一维图像
 

int ni = image.rows;
int nc = image.cols * image.channels();
if(iamge.isContimuous())
{
    nc = nc * ni;
    ni = 1;
}

7、使用迭代器访问像素

void colotReduce(Mat image, int diw = 64)
{
    /*MatIterator_<Ved3b> it;//这两种方法都可以创建迭代器对象
    cv::Mat_<Vec3b>::iterator it;*/
    MatIterator_<Ved3b> it = image.begin<Vec3b>();//注意模板参数的位置,括号前,函数名后
    MatIterator_<Ved3b> itend = image.end<Vec3b>();  
    for(;it != itend; it++)
    {
        (*it)[0] = (*it)[0]/div * div + div/2;
        (*it)[1] = (*it)[0]/div * div + div/2;
        (*it)[2] = (*it)[0]/div * div + div/2;
        //it->channel[0] = (it->channel[0]) /div * div + div/2;//这种形式也可以
    }   
}

使用迭代器看起来更简洁,还可以申明常量迭代器,只需替换为,MatConstIterator,以及const_iterator,还可以用Mat_实例的引用来获取迭代器位置。

8、各种方法特点

直接访问法:用时很长,适合随机访问像素,不适合扫描
指针访问:用时还好,没啥特点
迭代器访问:看起来直接明了,用时比较长

减少循环里的计算量,头线程,使用较短的语句都可以提高效率。

9、访问相邻像素

使用核心矩阵,锐化核,是滤波的一种。构造内核,然后调用filter2D函数。使用at方法访问内核的像素。saturate_cast方法用来防止溢出。使用该方法可以锐化图像。

10、简单图像运算

一般是对图像相加或者加权,可以使用add函数。简单图像运算的用途是创建特效图或者覆盖图像中的信息。
 

//这是常用的四种模式
//c[i]= a[i]+b[i] ;
cv: :add (imageA, imageB, resultC);
//c[i]= a[i]+k;
cv: :add (imageA, cv: :Scalar (k) , resultC) ;
//c[i]= k1*a[i] +k2*b[i]+k3;
cv: :addWeighted ( imageA,k1, imageB, k2,k3, resultC) ;
//c[i]= k*a[i] +b[i] ;
cv: :scaleAdd ( imageA, k, imageB, resultC) ;

也可以使用addWeighted(image1, 0.7, image2, 0.9, 0.0, result),类似add中的第三种。

11、通道分割

使用split(image1,planees)方法可以将image1这个完整的图像的三个通道分别复制到三个Mat类实例当中;使用mergc方法可以完成通道分离函数的逆过程。
 

vector<Mat> planes;        //这是三幅图像的向量
split(image1,planes);      //分离
merge(planes,result);      //组合

12、图像重映射

是指利用特定的映射关系来修改像素的位置,可以实现波浪、倾斜等特效。
 

void wave(const Mat& image; Mat result)
{
    Mat scrX(image.rows, image.cols, CV_32F);//用来存储原图像中每个像素点的x轴坐标
    Mat scrX(image.rows, image.cols, CV_32F);//用来存储原图像中每个像素点的y轴坐标
    for(int i = 0; i < image.rows; i++)      //通过循环装定各轴的像素点的位置
    {
        for(int j = 0; j < image.rows; j++)
        {
            srcX.at<float>(i,j) = j;         //at函数是用来获取指定像素位置的Mat数据
            srcY.at<float>(i,j) = i + 5*sin(j/10.0);
        }
    }
    remap(image, scrX, scrY, INTER_LINEAR);
}


上图是重映射的流程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值