2017-07-16 21:37:32 webzhuce 阅读数 2587
图像像素值的访问与修改是最常用的一种操作。VTK提供了两种访问图像像素值的访问。

一、直接访问

  第一种方法是直接发访问vtkImageData的数据数组。vtkImageData提供了GetScalarPointer()函数获取数据数组指针,该函数有三种形式:

virtual void* GetScalarPointer(int coordinates[3]);
virtual void* GetScalarPointer(int x, int y, int z);
virtual void* GetScalarPointer();

前两种形式根据给定的像素索引得到指定的像素值,注意返回的是第(x,y,z)个像素值的地址。而第三种形式是返回图像数据数组的头指针,然后根据头指针可以依次访问索引像素。

示例演示

使用GetScalarPointer遍历图像像素。

CMakeLists.txt代码

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(VisitImagePixelDirectlyExample)
FIND_PACKAGE(VTK REQUIRED)
INCLUDE(${VTK_USE_FILE})
ADD_EXECUTABLE(VisitImagePixelDirectlyExample   VisitImagePixelDirectlyExample.cpp)
TARGET_LINK_LIBRARIES(VisitImagePixelDirectlyExample ${VTK_LIBRARIES})

VisitImagePixelDirectlyExample.cpp文件代码如下:

/**********************************************************************

文件名: VisitImagePixelDirectlyExample.cpp
Copyright (c) 阿Bin先生. All rights reserved.
更多信息请访问: http://blog.csdn.net/webzhuce

**********************************************************************/

#include <vtkSmartPointer.h>
#include <vtkImageViewer2.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkBMPReader.h>
#include <vtkImageData.h>

int main(int argc, char* argv[])
{
    vtkSmartPointer<vtkBMPReader> reader =
        vtkSmartPointer<vtkBMPReader>::New();
    //测试图像:E:\TestData\masonry.bmp
    reader->SetFileName("E:\\TestData\\masonry.bmp");
    reader->Update();

    int dims[3];
    reader->GetOutput()->GetDimensions(dims);

    int nbOfComp;
    nbOfComp = reader->GetOutput()->GetNumberOfScalarComponents();

    for(int k=0; k<dims[2]; k++)
    {
        for(int j=0; j<dims[1]; j++)
        {
            for(int i=0; i<dims[0]; i++)
            {
                if(i<100 && j<100)
                {
                    unsigned char * pixel = 
                        (unsigned char *) ( reader->GetOutput()->GetScalarPointer(i, j, k) );
                    *pixel = 0;
                    *(pixel+1) = 0;
                    *(pixel+2) = 0;
                }
            }
        }
    }

    vtkSmartPointer<vtkImageViewer2> imageViewer =
        vtkSmartPointer<vtkImageViewer2>::New();
    imageViewer->SetInputConnection(reader->GetOutputPort());

    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
        vtkSmartPointer<vtkRenderWindowInteractor>::New();
    imageViewer->SetupInteractor(renderWindowInteractor);
    imageViewer->Render();
    imageViewer->GetRenderer()->ResetCamera();
    imageViewer->Render();

    imageViewer->GetRenderer()->SetBackground(1.0, 1.0, 1.0);
    imageViewer->SetSize(640, 480);
    imageViewer->GetRenderWindow()->SetWindowName("VisitImagePixelDirectlyExample");


    renderWindowInteractor->Start();

    return EXIT_SUCCESS;
}

运行结果:

这里写图片描述

  示例实现了将图像的100*100大小的区域设置为黑色。通过GetScalarPointer(i,j,k)函数获取访问图像像素值。需要注意的是,GetScalarPointer()函数返回的是void*类型,因此需要根据图像的实际类型进行强制转换。示例中将像素数组的头指针类型转换为unsigned char*。
  此外,需要注意的地方:VTK彩色以及矢量图像采用的是如下图所示的像素存储格式:
  
这里写图片描述

  因此在修改RGB图像以及矢量图像像素时,需要根据像素的元组的组分数目来访问。示例中先获得第(i,j,k)个像素的地址也就是R值的地址,然后将地址加1来访问后续G值和B值。如果对像素的元组组分不确定,可以通过函数GetNumberOfScalarComponent()获取,代码如下:

int nbOfComp = reader->GetOutput->GetNumberOfScalarComponent()

二、迭代器访问

  第二种方法是用vtkImageIterator类实现迭代器方法访问图像像素。该类是一个模板类,使用时,需要提供迭代的图像像素类型以及迭代的区域大小。

示例演示

CMakeLists.txt代码

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(VisitImagePixelIterativelyExample)
FIND_PACKAGE(VTK REQUIRED)
INCLUDE(${VTK_USE_FILE})
ADD_EXECUTABLE(VisitImagePixelIterativelyExampleVisitImagePixelIterativelyExample.cpp)
TARGET_LINK_LIBRARIES(VisitImagePixelIterativelyExample ${VTK_LIBRARIES})

VisitImagePixelIterativelyExample.cpp文件代码如下:

/**********************************************************************

文件名: VisitImagePixelIterativelyExample.cpp
Copyright (c) 阿Bin先生. All rights reserved.
更多信息请访问: http://blog.csdn.net/webzhuce

**********************************************************************/

#include <vtkSmartPointer.h>
#include <vtkImageViewer2.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkBMPReader.h>
#include <vtkImageData.h>
#include <vtkImageIterator.h>


int main(int argc, char* argv[])
{
    vtkSmartPointer<vtkBMPReader> reader =
        vtkSmartPointer<vtkBMPReader>::New();
    //测试图像:E:\\TestData\\masonry.bmp
    reader->SetFileName ( "E:\\TestData\\masonry.bmp" );
    reader->Update();

    int subRegion[6] = {0,100, 0, 100, 0, 0};
    vtkImageIterator<unsigned char> it(reader->GetOutput(), subRegion);

    while(!it.IsAtEnd())
    {
        unsigned char *inSI = it.BeginSpan();
        unsigned char *inSIEnd = it.EndSpan();

        while(inSI != inSIEnd)
        {
            *inSI = 255-*inSI;
            ++inSI;
        }
        it.NextSpan();
    }

    vtkSmartPointer<vtkImageViewer2> imageViewer =
        vtkSmartPointer<vtkImageViewer2>::New();
    imageViewer->SetInputConnection(reader->GetOutputPort());

    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
        vtkSmartPointer<vtkRenderWindowInteractor>::New();
    imageViewer->SetupInteractor(renderWindowInteractor);
    imageViewer->Render();
    imageViewer->GetRenderer()->ResetCamera();
    imageViewer->Render();

    imageViewer->GetRenderer()->SetBackground(1.0, 1.0, 1.0);
    imageViewer->SetSize(640, 480);
    imageViewer->GetRenderWindow()->SetWindowName("VisitImagePixelIterativelyExample");

    renderWindowInteractor->Start();

    return EXIT_SUCCESS;
}

运行结果:

这里写图片描述

   示例读取了一幅BMP图像,然后定义一个子区域。注意:在定义子区域时,不要超过图像的大小范围。然后根据图像类型unsigned char定义一个图像迭代器it,定义it时有两个参数:一个是要访问的图像,另一个是访问的图像区域。设置完毕,迭代器开始工作第一个while循环是判断迭代器是否结束。进入循环后,对于每一个迭代器it,又存在第二个循环,判断的是当前像素的元组(Tuple)是否迭代完毕。由于VTK中所有类型的图像格式都是vtkImageData,因此每个像素可能是标量,也可能是矢量。因此,每当访问到一个像素时需要迭代当前像素的元组。元组迭代时,inSI = it.BeginSpan()获取第一个组分,inEnd = it.EndSpan()表示组分迭代完毕。

2012-04-10 11:48:57 softard 阅读数 2960

随手翻了翻之前推荐的那本OpenCV2的教程,突然发现了之前看图像处理中的像素直方图的实现教程,原本就打算找时间自己实现的,没想到opencv是有这个功能的

而且这个功能可以辅助很多的图像处理和识别的算法过程,是值得看一看的

OpenCV对于像素直方图的统计有这么一个函数:

void calcHist(const Mat* arrays, 
             int narrays, 
             const int* channels, 
             InputArray mask, 
             OutputArray hist, 
             int dims, 
             const int* histSize, 
             const float** ranges, 
             bool uniform=true, 
             bool accumulate=false )

下面贴出官方的参数解释,我觉得很好明白的

Parameters
        arrays
– Source arrays. They all should have the same depth, CV_8U or CV_32F , and the
                        same size. Each of them can have an arbitrary number of channels.
        narrays – Number of source arrays.
        channels – List of the dims channels used to compute the histogram. The first ar-
                             ray channels are numerated from 0 to arrays[0].channels()-1 , the second ar-
                             ray channels are counted from arrays[0].channels() to arrays[0].channels() +
                             arrays[1].channels()-1, and so on.
       mask – Optional mask. If the matrix is not empty, it must be an 8-bit array of the same
                     size as arrays[i] . The non-zero mask elements mark the array elements counted in the
                     histogram.
      hist – Output histogram, which is a dense or sparse dims -dimensional array.
      dims – Histogram dimensionality that must be positive and not greater than CV_MAX_DIMS
                   (equal to 32 in the current OpenCV version).
      histSize – Array of histogram sizes in each dimension.
      ranges – Array of the dims arrays of the histogram bin boundaries in each dimension.
                       When the histogram is uniform ( uniform =true), then for each dimension i it is enough to
                      specify the lower (inclusive) boundary L0 of the 0-th histogram bin and the upper (exclusive)
                      boundary UhistSize[i]−1 for the last histogram bin histSize[i]-1 . That is, in case of a
                      uniform histogram each of ranges[i] is an array of 2 elements. When the histogram is not
                      uniform ( uniform=false ), then each of ranges[i] contains histSize[i]+1 elements:
                      L0 , U0 = L1 , U1 = L2 , ..., UhistSize[i]−2 = LhistSize[i]−1 , UhistSize[i]−1 . The array
                      elements, that are not between L0 and UhistSize[i]−1 , are not counted in the histogram.
       uniform – Flag indicatinfg whether the histogram is uniform or not (see above).
       accumulate – Accumulation flag. If it is set, the histogram is not cleared in the beginning
                                when it is allocated. This feature enables you to compute a single histogram from several
                                sets of arrays, or to update the histogram in time.

代码:

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;

class Histogram
{
        private:
                int histSize[1];//number of bins
                float hranges[2];//min and max pixel value
                const float* ranges[1];
                int channels[1];//only 1 channel used here
        public:
                Histogram()
                {
                        //Prepare arguments for 1D histogram
                        histSize[0] = 256;
                        hranges[0] = 0.0;
                        hranges[1] = 255.0;
                        ranges[0] = hranges;
                        channels[0] = 0;//by default , we look at channel 0
                }
                cv::MatND getHistogram(const cv::Mat &image)
                {
                        cv::MatND hist;

                        //Compute histogram
                        cv::calcHist(&image,
                                        1,
                                        channels,
                                        cv::Mat(),
                                        hist,
                                        1,
                                        histSize,
                                        ranges
                                        );
                     return hist;
                }
                //Compute the 1D histogram and returns an image of it
                cv::Mat getHistogramImage(const cv::Mat &image)
                {
                        //compute histogram first
                        cv::MatND hist = getHistogram(image);

                        //Get min and max bin values
                        double maxVal = 0;
                        double minVal = 0;
                        cv::minMaxLoc(hist,&minVal,&maxVal);

                        //Image on which to display histogram
                        cv::Mat histImg(histSize[0],histSize[0],CV_8U,cv::Scalar(255));

                        //set highest point at 90% of nbins
                        int hpt = static_cast<int>(0.9*histSize[0]);

                        //Draw a vertical line for each bin
                        for( int h = 0;h<histSize[0];h++)
                        {
                                float binVal = hist.at<float>(h);
                                int intensity = static_cast< int >(binVal * hpt
                                                                   / maxVal);

                                //This function draw a line between 2 points
                                cv::line(histImg,cv::Point(h,histSize[0]),
                                                        cv::Point(h,histSize[0] - intensity),
                                                        cv::Scalar::all(0));
                        }
                        return histImg;
                }
};


int main()
{
        //Read input image
        cv::Mat image = cv::imread("1.png",0);//open in b&w
        //the histogram object
        Histogram h;
        //Compute the histogram
        /*cv::MatND histo = h.getHistogram(image);
        for(int i = 0;i<256;i++)
        {
                cout<<"Value "<<i<<"="<<histo.at<float>(i) <<endl;
        }*/
        cv::namedWindow("Histogram");
        cv::imshow("Histogram",h.getHistogramImage(image));
        cv::waitKey(0);
        return 0;
}

需要注意的是imread这个函数后面的那个参数,不清楚的可以查阅官方的手册~

截图:



上面的代码只是读取的灰度图像反馈的1D的直方图,那么如果我是3通道的RGB/BGR图像呢?只需要做相应的修改然后函数便会返回3*256的一个Mat值

        private:
                int histSize[3];//number of bins
                float hranges[2];//min and max pixel value
                const float* ranges[3];
                int channels[3];//only 1 channel used here
        public:
                Histogram()
                {
                        //Prepare arguments for 1D histogram
                        histSize[0] = 256;histSize[1] = 256;histSize[2] = 256;
                        hranges[0] = 0.0;
                        hranges[1] = 255.0;
                        ranges[0] = hranges;ranges[1] =hranges;
                        ranges[2] =hranges;
                        channels[0] = 0;channels[1] = 1;channels[2] = 2;
                }
                cv::MatND getHistogram(const cv::Mat &image)
                {
                        cv::MatND hist;

                        //Compute histogram
                        cv::calcHist(&image,
                                        1,
                                        channels,
                                        cv::Mat(),
                                        hist,
                                        3,
                                        histSize,
                                        ranges
                                        );
                     return hist;
                }

但是在3通道的直方图计算过程中,有可能会觉得计算量过大了,同样可以使用稀疏矩阵(sparse matrix),calcHist同样支持

cv::SparseMat getSparseHistogram(const cv::Mat &image)
                {
                        cv::SparseMat hist(3,histSize,CV_32F);

                        //Compute histogram
                        cv::calcHist(&image,
                                        1,
                                        channels,
                                        cv::Mat(),
                                        hist,
                                        3,
                                        histSize,
                                        ranges
                                        );
                     return hist;
                }

接下来一段时间,就要回归水鱼的比赛了,OpenCV就要放一放

打算晚上写一下北大提供的PoseToPose函数的分析指导,由于自己的学识有限,很难分析全面,真的希望有人可以来指正我的错误~

                                                                                                                                             张巍骞

2012-4-10

2019-01-17 16:56:15 qq_34814092 阅读数 1020

Java OpenCV-4.0.0 图像处理8 图像像素点的获取与操作

java OpenCV-4.0.0 获取图像像素点并对像素点进行操作

package com.xu.image;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

/**  
 * 
 * @Title: OpenCV.java   
 * @Package com.xu.opencv   
 * @Description: TODO   
 * @author: xuhyacinth     
 * @date: 2019年1月17日 下午7:54:15   
 * @version V-1.0 
 * @Copyright: 2019 xuhyacinth
 *
 */
public class Test {

	static {
		//在使用OpenCV前必须加载Core.NATIVE_LIBRARY_NAME类,否则会报错
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
	}

	public static void main(String[] args) {
		getPixelImage();
	}

	/**
	 * OpenCV-4.0.0 获取图像像素点
	 * @return: void  
	 * @date: 2019年1月17日 下午8:24:07
	 */
	public static void getPixelImage_1() {
		Mat src = Imgcodecs.imread("C:\\Users\\Administrator\\Pictures\\0001.jpg");
		Mat dst = new Mat(src.size(),src.type());

		int channels = src.channels();//获取图像通道数
		double[] pixel = new double[3];//用于存放像素点

		for (int i = 0, rlen = src.rows(); i < rlen; i++) {
			for (int j = 0, clen = src.cols(); j < clen; j++) {
				if (channels == 3) {//图片为3通道即平常的(B,G,R)
					pixel = src.get(i, j).clone();
					pixel[0] = 255 - pixel[0];//B
					pixel[1] = 255 - pixel[1];//G
					pixel[2] = 255 - pixel[2];//R
					dst.put(i, j, pixel);
				} else {//图片为单通道
					dst.put(i, j, src.get(i, j).clone());
				}
			}
		}
		Imgproc.resize(src, src, new Size(src.cols()/2,src.rows()/2));
		HighGui.imshow("图片测试", dst);
		HighGui.waitKey(0);
	}

	/**
	 * OpenCV-4.0.0 获取图像像素点
	 * @return: void  
	 * @date: 2019年11月24日 下午8:24:07
	 */
	public static void getPixelImage_2() {
		Mat src = Imgcodecs.imread("C:\\Users\\Administrator\\Pictures\\0001.jpg");
		
		Mat dst = new Mat(src.size(),src.type());
		double[] pixel = new double[3];//用于存放像素点

		for (int i = 0 ,row = (int) src.size().height; i < row; i++) {
			for (int j = 0,col = (int) src.size().width; j < col; j++) {
				pixel = src.get(i, j).clone();
				pixel[0] = 255 - pixel[0];//B
				pixel[1] = 255 - pixel[1];//G
				pixel[2] = 255 - pixel[2];//R
				dst.put(i, j, pixel);
			}
		}
		HighGui.imshow("图片测试", dst);
		HighGui.waitKey(0);
	}

}


1

2017-02-13 10:27:21 u010303389 阅读数 951

像素间的基本关系

4邻域

一幅图像中,位于坐标(x,y)处的像素p有4个相邻的像素,分别位于其上下左右,其坐标如下:

(x+1,y)、(x-1,y)、(x,y+1)、(x,y-1)

用N4(p)表示像素p的4邻域



4邻域示意图

D邻域

像素p(x,y)的D邻域就是这个像素点的四个顶点对应的点,坐标如下:

(x+1,y+1)、(x+1,y-1)、(x-1,y+1)、(x-1,y-1)

用ND(p)表示像素p的D邻域:



D邻域示意图

8邻域

像素p(x,y)的8邻域就是这个像素的4邻域的点+ D邻域的点

用N8(p)表示像素p的8邻域:N8(p) = N4(p) + ND(p)



8邻域示意图
2019-07-15 20:27:49 weixin_39445525 阅读数 21

一、访问图像中像素的方法

解析:任何图像处理算法,都是从对每个像素的操作开始的,这是对图像处理算法的基本原理的理解和运用的基础,对Opencv中的函数理解和能够写出其相应功能的函数打下基础。

1.1 操作使用的主程序

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

// 函数声明
void ImgColorReduce(Mat& inputImage, Mat& outputImage, int div);

int main()
{
    // 载入图像并显示
    Mat originImage = imread("lena.png", 1);
    imshow("original", originImage);

    // 创建效果图
    Mat resultImage;
    resultImage.create(originImage.rows, originImage.cols, originImage.type());

    // 记录时间
    double timeClock = static_cast<double>(getTickCount());

    // 调用颜色空间缩减函数
    ImgColorReduce(originImage, resultImage, 32);

    // 输出时间
    timeClock = ((double)getTickCount() - timeClock) / getTickCount();
    cout << "run time:" << timeClock << "seconds" << endl;
    
    // 显示结果图
    imshow("result_colorReduce",resultImage);
    waitKey(0);
    return 0;
}

1.2 用指针访问图像元素(run time:3.37536e-08seconds)

利用C语言中的操作符[],速度快,但是相对理解抽象。

// 用指针访问像素
void ImgColorReduce(Mat& inputImage, Mat& outputImage, int div)
{
    outputImage = inputImage.clone();  // 参数克隆
    int rowNum = outputImage.rows;      // 行

    //列(每一行元素的个数=列x通道数)
    int colNUm = outputImage.cols*outputImage.channels(); 
    for (int i = 0; i < rowNum; i++)  // 遍历行
    {
        uchar* data = outputImage.ptr<uchar> (i); // 获得行的首地址
        for(int j = 0; j < colNUm; j++) // 遍历列
        {
            data[j] = data[j] / div*div + div/2;
        }

    }

}

1.3 迭代器访问图像像素(run time:5.51654e-08seconds)

需要获取图像矩阵的开始和结尾,遍历开始和结尾,通过*操作符访问相关内容,相对于指针比较安全。

// 用迭代器访问像素
void ImgColorReduce(Mat& inputImage, Mat& outputImage, int div)
{
    outputImage = inputImage.clone(); //  克隆参数
    // 获取初始和结尾迭代器
    Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();
    Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();

    // 通道颜色处理
    for(; it != itend; ++it)
    {
        (*it)[0] = (*it)[0]/div*div + div/2;
        (*it)[1] = (*it)[1]/div*div + div/2;
        (*it)[2] = (*it)[2]/div*div + div/2;
    }

}

1.4 动态地址访问图像像素(run time:3.32831e-08seconds)

相对于比较好理解,方法直观。其中cols 和rows 对应图像的宽和高。成员函数at(int y, int x)用来存取图像元素。

对于一个包含彩色图像的Mat ,会返回一个由三个8位数组组成的向量。OpenCV将此类型的向量定义为Vec3b,即由三个unsigned char  组成的向量。OpenCV中的颜色分量是BGR。

// 动态地址访问图像像素
void ImgColorReduce(Mat& inputImage, Mat& outputImage, int div)
{
    outputImage = inputImage.clone(); //  克隆参数
    // 获取图像的行列
    int rowNum = outputImage.rows;
    int colNum = outputImage.cols;

    // 处理图像像素值
    for(int i = 0; i < rowNum; i++)
    {
        for (int j = 0; j < colNum; j++)
        {
            //分别处理蓝、绿、红通道
            outputImage.at<Vec3b>(i,j)[0] = outputImage.at<Vec3b>(i,j)[0]/div*div + div/2;
            outputImage.at<Vec3b>(i,j)[1] = outputImage.at<Vec3b>(i,j)[1]/div*div + div/2;
            outputImage.at<Vec3b>(i,j)[2] = outputImage.at<Vec3b>(i,j)[2]/div*div + div/2;
        }
    }

}

1.5 编译和运行结果

没有更多推荐了,返回首页