概述:
本例程为利用OpenCV通过操控内存数据,创建一个光滑渐变的图像。
我们会知道如何对图像数据进行访问修改以及操作。
核心内容实践:
Step:1: 如何访问指定坐标处的像素点
我们创建的内存图像数组可以记住图像进行理解
由于每个像素有三个通道,那么指定坐标处的数组索引就变成了 像素索引 = (y * width * x) * 通道数。定位到该像素索引之后,我们便可以去按照像素格式,逐通道的进行像素更改,利用指针,我们可以很优雅的实现这个逻辑。
/************************************
*
* 操控指定内存图像指定位置的像素值
*
************************************/
bool SetBGRPixel(unsigned char* pImage,int x,int y, int nWidth, int nHeight, unsigned char R, unsigned G, unsigned B)
{
//nPixelIndex,定位到指定位置的内存图像的索引,第Y行,第X列
int nPixelIndex = (y * nWidth + x) * 3;
//赋值像素位置B通道参数
pImage[nPixelIndex ] = B;
//赋值像素位置G通道参数
pImage[nPixelIndex + 1] = G;
//赋值像素位置R通道参数
pImage[nPixelIndex + 2] = R;
return true;
}
Step:2: 如何创建一个平滑的像素渐变
关于如何平滑的过渡,我们可以看下面这个图,对距离和像素进行插值计算。
插值的思想在图形图像中经常会遇到,通过合理的插值,我们可以模拟出很多中间过程而不用逐个的去求中间每个值。
我们知道A的值,也知道B处的值,那么求得一个A,B之间的平滑的过渡区域那就是在我们要走的这么多步之中,每次都恰好走了一个step,由于内存像素只认整数,在step的过程中需要用一个浮点数来记录当前步进到多少,然后用的时候将这个值规整到指定像素值。
//计算行渐变的步进值,double类型保留了精度
double nPixelColorAdd = 255.0 / nIMGWidth;
//遍历内存图像数组的height
for (int nYIndex = 0; nYIndex < nIMGHeight; nYIndex++)
{
//定义步进图像的像素值通道参数
double dRValue = 0.0;
double dGValue = 0.0;
double dBValue = 0.0;
//遍历内存图像数组的width
for (int nXIndex = 0; nXIndex < nIMGWidth; nXIndex++)
{
//对图像的像素值进行规整
unsigned char uRValue = (unsigned char)dRValue;
unsigned char uGValue = (unsigned char)dGValue;
unsigned char uBValue = (unsigned char)dBValue;
//更改指定坐标处的像素值
SetBGRPixel(theMemoryImage, nXIndex, nYIndex, nIMGWidth, nIMGHeight,uRValue,uGValue,uBValue);
//步进指定通道图像的像素值
dGValue += nPixelColorAdd;
}
}
工程示例
/***************************************************************************
*
* 本例程是一個如何操控内存图像的示例,并在章节1的基础上将过程动态的显示出来
* 2018-7-21 in Xi'an
* by hzhy
*
*****************************************************************************/
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <iostream>
#pragma comment(lib,"opencv_core341d.lib")
#pragma comment(lib,"opencv_highgui341d.lib")
using namespace std;
/**窗口Name**/
const char* windowName = "BGR Window ... Press ESC Quit";
/************************************
*
* 創建BGR內存圖像
*
************************************/
unsigned char* CreateBGRMemoryImage(int nWidth, int nHeight);
/************************************
*
* 窗體顯示BGR內存圖像
*
************************************/
bool ShowBGRImage(unsigned char* pImage, int nWidth, int nHeight);
/************************************
*
* 操控指定内存图像指定位置的像素值
*
************************************/
bool SetBGRPixel(unsigned char* pImage,int x,int y, int nWidth, int nHeight, unsigned char R, unsigned G, unsigned B);
int main(int argc, char ** argv)
{
//设置显示窗体的size
int nIMGWidth = 640;
int nIMGHeight = 480;
double nPixelColorAdd = 255.0 / nIMGWidth;
//create memory image buffer
unsigned char* theMemoryImage = CreateBGRMemoryImage(nIMGWidth, nIMGHeight);
for (int nYIndex = 0; nYIndex < nIMGHeight; nYIndex++)
{
double dRValue = 0.0;
double dGValue = 0.0;
double dBValue = 0.0;
for (int nXIndex = 0; nXIndex < nIMGWidth; nXIndex++)
{
unsigned char uRValue = (unsigned char)dRValue;
unsigned char uGValue = (unsigned char)dGValue;
unsigned char uBValue = (unsigned char)dBValue;
SetBGRPixel(theMemoryImage, nXIndex, nYIndex, nIMGWidth, nIMGHeight,uRValue,uGValue,uBValue);
dGValue += nPixelColorAdd;
}
}
ShowBGRImage(theMemoryImage, nIMGWidth, nIMGHeight);
cvWaitKey(0);
delete[] theMemoryImage;
return 0;
}
unsigned char* CreateBGRMemoryImage(int nWidth, int nHeight)
{
int nImgChannel = 3;
int nPixelIndex = 0;
unsigned char* pMemoryImage = new unsigned char[nWidth * nHeight * nImgChannel];
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
pMemoryImage[nPixelIndex++] = 0; // B channel
pMemoryImage[nPixelIndex++] = 0; // G channel
pMemoryImage[nPixelIndex++] = 255; // R channel
}
return pMemoryImage;
}
bool ShowBGRImage(unsigned char* pImage, int nWidth, int nHeight)
{
IplImage* pSrcImage = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 3);
unsigned char* pImageData = (unsigned char*)pSrcImage->imageData;
int nImageIndex = 0;
//fill memory img to cvbuffer
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
int nCurPixelIndex = nYIndex * pSrcImage->widthStep + nXIndex * 3;
pImageData[nCurPixelIndex++] = pImage[nImageIndex++];
pImageData[nCurPixelIndex++] = pImage[nImageIndex++];
pImageData[nCurPixelIndex++] = pImage[nImageIndex++];
}
cvShowImage(windowName, pSrcImage);
cvReleaseImage(&pSrcImage);
return true;
}
bool SetBGRPixel(unsigned char* pImage,int x,int y, int nWidth, int nHeight, unsigned char R, unsigned G, unsigned B)
{
int nPixelIndex = (y * nWidth + x) * 3;
pImage[nPixelIndex ] = B;
pImage[nPixelIndex + 1] = G;
pImage[nPixelIndex + 2] = R;
return true;
}
效果图
求得一个红色通道的步进效果图
dRValue += nPixelColorAdd;
求得一个绿色通道的步进效果图
dGValue += nPixelColorAdd;
求得一个蓝色通道的步进效果图
dBValue += nPixelColorAdd;
结语:
通过本章节的学习,我们学会了如何访问内存数组,并利用一个步进公式,得到了一个过渡色的内存图像,展示了出来。