Contents
  1. I. Read and Write Operations
  2. 1.1 Array Traversal
  3. 1.2 Pointer Traversal
  4. II. Arithmetic Operations
  5. 2.1 Pixels
  6. 2.2 Image Arithmetic Operation APIs
  7. III. Logical Operations
  8. 3.1 Basics—Truth Table
  9. 3.2 Draw a Rectangle
  10. 3.3 Logical Operations

I. Read and Write Operations

1.1 Array Traversal

  Since an image is essentially a Mat matrix, its pixels can be read and written by using array traversal to access every element in the Mat matrix. Note, however, that grayscale and color images have different numbers of channels: grayscale images have one channel, while color images have three channels. Reading and writing pixels therefore involves two cases: reading and writing grayscale-image pixels, and reading and writing color-image pixels.

① Reading and writing grayscale-image pixels

  Each pixel in a grayscale image corresponds to one value in the Mat matrix, so accessing a grayscale-image pixel is equivalent to accessing an element of the Mat matrix. The syntax is as follows:

//读灰度图像素
int pv = image.at<uchar>(row, col);
//写灰度图像素(反转颜色)
image.at<uchar>(row, col) = 255 - pv;

  Since each grayscale-image pixel occupies 1 byte (0-255), uchar is used. Here, row represents the number of rows in the Mat matrix, and col represents the number of columns.

② Reading and writing color images

  Each pixel in a color image corresponds to three values in the Mat matrix. The access method is similar to that for a grayscale image.

//读彩色图像素
Vec3b bgr = image.at<Vec3b>(row, col);
//写彩色图像素(反转颜色)
image.at<Vec3b>(row, col)[0] = 255 - bgr[0];
image.at<Vec3b>(row, col)[1] = 255 - bgr[1];
image.at<Vec3b>(row, col)[2] = 255 - bgr[2];

  Because accessing a color-image pixel requires reading three values at once, we use the Vec3b structure (which can be viewed as an array). The three values obtained through the access can be stored directly in a variable defined with the Vec3b structure.   If the values of color-image pixels are integers, Vec3i must be used; if they are floating-point values, vec3f must be used.

③ Example program

void MyDemo::pixelVisit_Demo(Mat& image) {
	int w = image.cols;
	int h = image.rows;
	int dims = image.channels();
	for (int row = 0; row < h; row++) {
		for (int col = 0; col < w; col++) {

			//灰度图像
			if (dims == 1) {
				int pv = image.at<uchar>(row, col);
				image.at<uchar>(row, col) = 255 - pv;
			}

			//彩色图像
			if (dims == 3) {
				Vec3b bgr = image.at<Vec3b>(row, col);
				image.at<Vec3b>(row, col)[0] = 255 - bgr[0];
				image.at<Vec3b>(row, col)[1] = 255 - bgr[1];
				image.at<Vec3b>(row, col)[2] = 255 - bgr[2];
			}
		}
	}
	imshow("Pixel Visit Demo", image);
}

Array Traversal

1.2 Pointer Traversal

  The principle of pointer traversal is similar to that of array traversal. Define a pointer to the start address of the current row, and then use this pointer to traverse and access all pixels in that row.

void MyDemo::pixelVisit_Demo(Mat& image) {
	int w = image.cols;
	int h = image.rows;
	int dims = image.channels();
	for (int row = 0; row < h; row++) {
		uchar* current_row = image.ptr<uchar>(row);
		for (int col = 0; col < w; col++) {

			//灰度图像
			if (dims == 1) {
				*current_row++ = 255 - *current_row;
			}

			//彩色图像
			if (dims == 3) {
				*current_row++ = 255 - *current_row;
				*current_row++ = 255 - *current_row;
				*current_row++ = 255 - *current_row;
			}
		}
	}
	imshow("Pixel Visit Demo", image);
}

Here, current_row points to the start address of each row as the loop progresses. *current_row++ = 255 - *current_row; means inverting the color of the value pointed to by current_row (a pixel in a grayscale image or one channel of a pixel in a color image), then incrementing the pointer by +1 so that it points to the next pixel or the next channel of the pixel.

II. Arithmetic Operations

2.1 Pixels

  Addition, subtraction, multiplication, and division can be performed directly on the Mat matrix of an image (note that addition for a color image requires Scalar). The result of addition/subtraction is an increase/decrease in image brightness, and the same applies to multiplication/division. However, processing may cause pixel values to exceed the range (0~255); the saturate_cast function can be used for clipping.

//image * m -> dst
void MyDemo::operators_Demo(Mat& image) {
	Mat m = Mat::zeros(image.size(), image.type());
	m = Scalar(20, 20, 20);
	Mat dst = Mat::zeros(image.size(), image.type());
	
	int w = image.cols;
	int h = image.rows;
	int dims = image.channels();

	for (int row = 0; row < h; row++) {
		for (int col = 0; col < w; col++) {

			//灰度图像
			if (dims == 1) {
				int pv = image.at<uchar>(row, col);
				image.at<uchar>(row, col) = 255 - pv;
			}

			//彩色图像
			if (dims == 3) {
				Vec3b p1 = image.at<Vec3b>(row, col);
				Vec3b p2 = m.at<Vec3b>(row, col);
				dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(p1[0] * p2[0]);
				dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(p1[1] * p2[1]);
				dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(p1[2] * p2[2]);
			}
		}
	}
	imshow("operator",dst);
}

Pixels

2.2 Image Arithmetic Operation APIs

OperationFunction
Additionadd(img1, img2, imgout);
Subtractionsubtract(img1, img2, imgout);
Multiplicationmultiply(img1, img2, imgout);
Divisiondivide(img1, img2, imgout);
void MyDemo::operators_Demo(Mat& image) {
	Mat dst = Mat::zeros(image.size(), image.type());
	Mat m = Mat::zeros(image.size(), image.type());
	m = Scalar(20, 20, 20);

	//add(image, m, dst);
	//subtract(image, m, dst);
	multiply(image, m, dst);
	//divide(image, m, dst);

	imshow("operator",dst);

}

III. Logical Operations

3.1 Basics—Truth Table

ABANDORXOR
00000
10011
01011
11110

3.2 Draw a Rectangle

  To display the results of pixel-level logical operations more intuitively, we can draw two rectangles and perform logical operations on their intersecting region.   Drawing a rectangle is very simple: first create a blank image, then call the rectangle function.

rectangle(m1, Rect(50, 50, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);
rectangle(被处理图像, 左上点坐标, 颜色, 线宽, 线型, 坐标点的小数点位数);

The example program is as follows:

void MyDemo::bitWise_Demo(Mat& image) {
	Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);
	Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);
	rectangle(m1, Rect(50, 50, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);
	rectangle(m2, Rect(100, 100, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);
	imshow("m1", m1);
	imshow("m2", m2);
}

3.3 Logical Operations

OperationFunction
ANDbitwise_and(m1, m2, dst);
ORbitwise_or(m1, m2, dst);
NOTbitwise_not(m1, dst);
XORbitwise_xor(m1, m2, dst);

Taking the “AND” operation as an example, the test code is as follows:

void MyDemo::bitWise_Demo(Mat& image) {
	Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);
	Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);
	rectangle(m1, Rect(50, 50, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);
	rectangle(m2, Rect(100, 100, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);
	imshow("m1", m1);
	imshow("m2", m2);
	Mat dst;
	bitwise_and(m1, m2, dst);
	imshow("bitWise", dst);
}
RegionColor
BackgroundScalar(0, 0, 0)
Rectangle 1Scalar(255, 255, 0)
Rectangle 2Scalar(0, 255, 255)
Intersecting regionScalar(0, 255, 0)
Other regionsScalar(0, 0, 0)

Logical Operations

  The other “OR,” “NOT,” and “XOR” operations are similar. Those interested can try them on their own.