OpenCV中 CV_IMAGE_ELEM 详细说明及其他方式对像素操作的方法使用,alpha混合(透明混合)函数(附源码)

1、实际中的使用案例

void Threshold(IplImage *Image, IplImage *Image_O)
{
	int thresMax = 0, thresMin = 255,;
    int i = 0, j = 0, t = 0;
	for (j = 0; j < Image->height; j++)//图像高度方向
		for (i = 0; i < Image->width; i++)//图像宽度方向
		{
			if (CV_IMAGE_ELEM(Image, uchar, j, i) > thresMax)  
				thresMax = CV_IMAGE_ELEM(Image, uchar, j, i);  //获取图像中最大像素值 
			else if (CV_IMAGE_ELEM(Image, uchar, j, i) < thresMin)   
				thresMin = CV_IMAGE_ELEM(Image, uchar, j, i);  //获取图像中最小像素值
		}
}

2、详细参数解释:

  CV_IMAGE_ELEM 是一个宏函数,基本形式:

/** 对于多通道图像(col),在对(col,row)位置像素的引用中,应乘以通道数*/
#define CV_IMAGE_ELEM( image, elemtype, row, col )       \
    (((elemtype*)((image)->imageData + (image)->widthStep*(row)))[(col)])

                                CV_IMAGE_ELEM(image,elemtype,row,col)

变量解释注意
image

 image为指针数组 

指针数组
elemtypeelemtype为数据的存取类型数据类型
rowrow为图像的高度,范围:0~heightrow 和col顺序不能混淆
colcol为图像的宽度,范围:0~widthrow 和col顺序不能混淆

一句话来解释就是:读取图像中坐标为(col,row)的像素的数据信息。

3、读取图像中坐标位置数据信息的其他方式

3.1 Mat.at<float>(i,j)

/*Mat.at<float>(i,j)法*/

Mat inverseColor1(Mat srcImg)
{

    Mat temp = srcImg.clone();//克隆图像
    int row = temp.rows;//获取图像的总行数
    int col = temp.cols;//获取图像的总列数
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col;j++)
        {
            //temp.at<cv::Vec3b>(i, j)[0] 获取在第(i行, j列)位置的像素值
            temp.at<cv::Vec3b>(i, j)[0] = 255 - temp.at<cv::Vec3b>(i, j)[0];//R通道反色
            temp.at<cv::Vec3b>(i, j)[1] = 255 - temp.at<cv::Vec3b>(i, j)[1];//G通道反色
            temp.at<cv::Vec3b>(i, j)[2] = 255 - temp.at<cv::Vec3b>(i, j)[2];//B通道反色
        }
    }
    return temp;
}

        temp.at<cv::Vec3b>(i, j)[n]表示彩色3通道图像中i行j列第k个通道的颜色像素值,其中<cv::Vec3b>是opencv里面的像素值类型。
其函数模板为typedef Vec<uchar,3>Vec3b,表示3通道uchar。
注意:Mat.at虽然实现比较简单,但是在速度上比较慢。

3.2 指针遍历Mat::ptr<type>

/*指针遍历Mat::ptr<type>*/

Mat inverseColor2(Mat srcImg)
{
    Mat temp = srcImg.clone();
    int row = temp.rows;
    int col = temp.cols;
    int nStep = temp.cols * temp.channels();//总列数据=图像总列数  *  通道数
    for (int i = 0; i < row; i++)
    {
        uchar *pSrcData = srcImg.ptr<uchar>(i);//一次获取第i行的数据,存放在uchar数组中
        uchar *pResuiltData = temp.ptr<uchar>(i);//一次获取第i行的数据,存放在uchar数组中
        for (int j = 0; j < nStep;j++)
        {
        //saturate_cast<uchar>  防呆机制,例如小于0的数据会调整为0,大于255的数据会调整为255
            pResuiltData[j] = saturate_cast<uchar>(255 - pSrcData[j]);          
        }
    }
    return temp;
}

Opencv提供了ptr的方法,用于表示遍历图像的每一个字节,默认返回值为uchar* 或者const uchar* 。
来自模板temp<typename _Tp>_Tp*Mat::ptr(int i0=0),i0代表是以零行为基准的索引,函数返回指向特定矩阵的uchar* 或指针。

ptr速度优于at和MatIterator_,是最常用的一种方式。
相比Mat.at方法而言,操作方式复杂,运用到指针操作。

        MatConstIterator_和MatIterator_是两个迭代器;
        MatConstiterator      将迭代器设置为构造函数。
        MatConstIterator_和MatIterator_   两者是将迭代器设置为矩阵指定元素的构造函数。
        迭代器MatIterator_速度是五种方式里面的最慢的,操作方式比较复杂。

3.3  迭代器MatIterator_

/* 迭代器MatIterator_ */
Mat inverseColor3(Mat srcImg)
{
    Mat temp = srcImg.clone();
    MatConstIterator_<Vec3b> srcIterStart = srcImg.begin<Vec3b>();//表示指向迭代器的起始位置。
    MatConstIterator_<Vec3b> srcIterEnd = srcImg.end<Vec3b>();//表示指向迭代器的终止位置
    MatIterator_<Vec3b> resIterStart = temp.begin<Vec3b>();//表示指向迭代器的起始位置。
    MatIterator_<Vec3b> resIterEnd = temp.end<Vec3b>();//表示指向迭代器的终止位置
    //通过起始起始和终止位置之间,移动指针完成对图像的像素遍历。
    while (srcIterStart != srcIterEnd)
    {
        (*resIterStart)[0] = 255 - (*srcIterStart)[0];//R
        (*resIterStart)[1] = 255 - (*srcIterStart)[1];//G
        (*resIterStart)[2] = 255 - (*srcIterStart)[2];//B
        srcIterStart++;
        resIterStart++;
    }
    return temp;
}

3.4 连续操作法 isContinouous

/* 连续操作法 isContinouous */

Mat inverseColor4(Mat src)
{
    int row = src.rows;
    int col = src.cols;
    cv::Mat temp = src.clone();
    // 判断是否是连续图像,即是否有像素填充
    if (src.isContinuous() && temp.isContinuous())
    {
        row = 1;
        // 按照行展开
        col = col * src.rows * src.channels();
    }
    // 遍历图像的每个像素
    for (int i = 0; i < row; i++)
    {
        // 设定图像数据源指针及输出图像数据指针
        const uchar* pSrcData = src.ptr<uchar>(i);
        uchar* pResultData = temp.ptr<uchar>(i);
        for (int j = 0; j < col; j++)
        {
            *pResultData++ = 255 - *pSrcData++;
        }
    }
    return temp;
}

        用Mat存储一幅图像时,若图像在内存中是连续存储的(Mat对象的isContinuous == true),则可以将图像的数据看成是一个一维数组,而data(uchar*)成员就是指向图像数据的第一个字节的,因此可以用data指针访问图像的数据,从而加速Mat图像的访问速度。

        但是往往图像行与行之间的存储可能是不连续的,进行像素值遍历,很大程度上造成数据指针移动的浪费。Mat提供了一个检测是否连续的函数isContinuous(),1xN的图像矩阵是连续的。当图像元素连续时,可以看成一行,按行展开,利用指针来获取起始行的位置,进行遍历,
节省了寻址的时间。

        例:一张M x N的图像按行展开后,成为了1 x( N x M) 列的连续像素点。isContinuous()的方法操作难度居中,推荐使用(备注:图像必须是连续的才能使用)。

        注意:一般经过裁剪的Mat图像,都不再连续了,如cv::Mat crop_img = src(rect);crop_img 是不连续的Mat图像,如果想转为连续的,最简单的方法,就是将不连续的crop_img 重新clone()一份给新的Mat就是连续了。

3.5   LTU查表法

/* LTU查表法 */

Mat inverseColor5(Mat src)
{
    int row = src.rows;
    int col = src.cols;
    Mat temp = src.clone();  
    uchar LutTable[256 * 3]; // 建立LUT 反色table
    //初始化
    for (int i = 0; i < 256; ++i)
    {
        LutTable[i * 3] = 255 - i;
        LutTable[i * 3+1] = 255 - i;
        LutTable[i * 3+2] = 255 - i;
    }
    Mat lookUpTable(1, 256, CV_8UC3, LutTable);
    // 应用索引表进行查找
    LUT(src, lookUpTable, temp);
    return temp;
}

/*LTU查表反色处理(单通道)*/
Mat inverseColor5_1(Mat src)
{
    int row = src.rows;
    int col = src.cols;
    Mat temp = src.clone();
    uchar LutTable[256]; // 建立LUT 反色table
    for (int i = 0; i < 256; ++i)
    {
        LutTable[i] = 255 - i;
    }
    Mat lookUpTable(1, 256, CV_8U);
    uchar* pData = lookUpTable.data;
    // 建立映射表
    for (int i = 0; i < 256; ++i)
    {
        pData[i] = LutTable[i];
    }
    // 应用索引表进行查找
    LUT(src, lookUpTable, temp);
    return temp;
}

LUT函数作用:数组的查找表转换。函数LUT以来自查找表的值填充输出数组。每个像素的索引是从输入数组中获取的,此函数处理src的每个元素逻辑如下:

                                                        dst(I)←lut(src(I) + d)

参数解释:src输入阵列的8位元素。256个元素的lut查找表;

在多通道输入阵列的情况下,该表应该有一个单独的通道(在本例中为所有通道使用相同的表)或与输入阵列相同的通道数量。与src相同大小和通道数量的dst输出阵列,和lut的深度相同。总之,单通道使用单个映射表;多通道输入时,可以多个通道使用同一个映射表,也可以使用多个映射表(看个人需求),上例三通道图像使用同一个表,输出图像的通道和映射表的深度有关。

4 如何实现一个alpha混合(透明混合)函数

/*如何实现一个alpha混合(透明混合)函数*/
template<typename T>
void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst)
{
    const float alpha_scale = (float)std::numeric_limits<T>::max(),
                inv_scale = 1.f/alpha_scale;
    CV_Assert( src1.type() == src2.type() &&
               src1.type() == CV_MAKETYPE(DataType<T>::depth, 4) &&
               src1.size() == src2.size());
    Size size = src1.size();
    dst.create(size, src1.type());
    // here is the idiom: check the arrays for continuity and,
    // if this is the case,
    // treat the arrays as 1D vectors
    if( src1.isContinuous() && src2.isContinuous() && dst.isContinuous() )
    {
        size.width *= size.height;
        size.height = 1;
    }
    size.width *= 4;
    for( int i = 0; i < size.height; i++ )
    {
        // when the arrays are continuous,
        // the outer loop is executed only once
        const T* ptr1 = src1.ptr<T>(i);
        const T* ptr2 = src2.ptr<T>(i);
        T* dptr = dst.ptr<T>(i);
        for( int j = 0; j < size.width; j += 4 )
        {
            float alpha = ptr1[j+3]*inv_scale, beta = ptr2[j+3]*inv_scale;
            dptr[j] = saturate_cast<T>(ptr1[j]*alpha + ptr2[j]*beta);
            dptr[j+1] = saturate_cast<T>(ptr1[j+1]*alpha + ptr2[j+1]*beta);
            dptr[j+2] = saturate_cast<T>(ptr1[j+2]*alpha + ptr2[j+2]*beta);
            dptr[j+3] = saturate_cast<T>((1 - (1-alpha)*(1-beta))*alpha_scale);
        }
    }
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林中青木

原创不易,请多多支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值