图像处理:图片二值化学习,以及代码中如何实现

目录

1、了解下图片二值化的含义

2、进行图像二值化处理的方法

3、如何选择合适的阈值进行二值化

4、实现图片二值化(代码)

(1)是使用C++和OpenCV库实现:

(2)纯C++代码实现,不要借助其他库

(3)图片处理的一个实例:


1、了解下图片二值化的含义

(1)图片二值化是一种图像处理技术,它将彩色或灰度图像转换为只包含两个颜色的图像,通常是黑色和白色。这种转换是通过将图像中的每个像素的灰度值与一个阈值进行比较来实现的。

(2)在二值化过程中,如果像素的灰度值大于或等于阈值,则将该像素设置为白色(或亮色),否则将其设置为黑色(或暗色)。这样,图像中的每个像素都被映射到黑色或白色之一,从而产生了一个只有两种颜色的二值图像。

(3)二值化可以用于很多应用,例如文字识别、图像分割、形状检测等。通过将图像转换为二值图像,可以突出显示目标物体的轮廓和特征,并简化后续的图像处理任务。

2、进行图像二值化处理的方法

进行图像二值化处理的方法有多种,下面介绍两种常用的方法:

(1)全局阈值法(Global Thresholding):

        该方法假设整个图像的前景和背景具有明显的灰度差异,并且通过选择一个全局阈值来将图像分为两个部分。

具体步骤如下:

        1)将彩色或灰度图像转换为灰度图像。

        2)选择一个合适的全局阈值。

        3)遍历图像中的每个像素,如果像素的灰度值大于等于阈值,则将其设置为白色;否则将其设置为黑色。

(2)自适应阈值法(Adaptive Thresholding):

        该方法考虑到图像不同区域的光照条件可能不同,因此使用局部阈值来对图像进行分割。

具体步骤如下:

        1)将彩色或灰度图像转换为灰度图像。

        2)将图像分成多个小的局部区域。

        3)对每个局部区域计算一个适应性阈值。

        4)遍历图像中的每个像素,根据所在的局部区域的阈值将像素设置为黑色或白色。

这些方法可以使用图像处理库或软件实现,例如OpenCV、Python的PIL库等。具体的实现方式和参数选择会根据具体的图像和需求而有所不同。

3、如何选择合适的阈值进行二值化

选择合适的阈值进行图像二值化是一个关键的步骤,下面介绍几种常用的阈值选择方法:

(1)固定阈值法(Fixed Thresholding):该方法是最简单的阈值选择方法,直接根据经验或试验确定一个固定的阈值。例如,将阈值设为128,即大于等于128的像素设置为白色,小于128的像素设置为黑色。

(2)Otsu's 阈值法:Otsu's 阈值法是一种自动选择阈值的方法,它能够找到一个最佳的阈值,使得分割后的图像类间方差最大化。这种方法适用于具有双峰直方图的图像,其中前景和背景的灰度值分布明显不同。

(3)自适应阈值法(Adaptive Thresholding):自适应阈值法根据图像局部区域的灰度特性来选择阈值。它将图像分成多个小的局部区域,并对每个区域计算一个适应性阈值。这种方法适用于光照条件不均匀的图像。

(4)大津法与自适应阈值法的结合:有时候可以结合使用大津法和自适应阈值法,先使用大津法确定一个全局阈值,然后再使用自适应阈值法对图像进行细分割。

选择合适的阈值方法取决于图像的特性和需求。一般来说,如果图像具有明显的前景和背景差异,固定阈值法可能是一个简单有效的选择。如果图像的灰度分布复杂或光照条件不均匀,可以考虑使用自适应阈值法或Otsu's 阈值法。

4、实现图片二值化(代码)

(1)是使用C++和OpenCV库实现:

#include <opencv2/opencv.hpp>

int main() 
{
    // 读取图像
    cv::Mat image = cv::imread("input.jpg", cv::IMREAD_GRAYSCALE);

    // 检查图像是否成功读取
    if (image.empty()) {
        std::cout << "无法读取图像文件" << std::endl;
        return -1;
    }

    // 应用全局阈值法进行二值化
    cv::Mat binaryImage;
    double thresholdValue = 128; // 阈值设为128

    double maxValue = 255; // 最大值设为255

    cv::threshold(image, binaryImage, thresholdValue, maxValue, cv::THRESH_BINARY);

    // 显示原始图像和二值化后的图像
    cv::imshow("Original Image", image);
    cv::imshow("Binary Image", binaryImage);
    cv::waitKey(0);

    return 0;
}
(2)纯C++代码实现,不要借助其他库

#include <iostream>
#include <fstream>

#pragma pack(push, 1)
struct BMPHeader {
	char signature[2];
	int fileSize;
	int reserved;
	int dataOffset;
	int headerSize;
	int width;
	int height;
	short planes;
	short bitsPerPixel;
	int compression;
	int imageSize;
	int xPixelsPerMeter;
	int yPixelsPerMeter;
	int colorsUsed;
	int importantColors;
};
#pragma pack(pop)

bool Bmp2Binarization()
{
	// 读取BMP文件头信息
	ifstream file(("oldBmp.bmp"), std::ios::binary);

	if (!file) {
		cout << "无法打开BMP文件" << endl;
		return -1;
	}

	BMPHeader header;
	file.read(reinterpret_cast<char*>(&header), sizeof(BMPHeader));

	// 检查文件是否为24位真彩色BMP图像
	if (header.signature[0] != 'B' || header.signature[1] != 'M') {
		cout << "不支持的BMP文件格式" << endl;
		return -1;
	}

	if (header.bitsPerPixel != 24) {
		cout << "不支持的像素位数" << endl;
		return -1;
	}

	// 计算图像数据的行字节数
	int rowSize = ((header.width * 3 + 3) / 4) * 4;

	// 创建存储图像数据的数组
	unsigned char* image_data = new unsigned char[rowSize * header.height];

	// 读取图像数据
	file.read(reinterpret_cast<char*>(image_data), rowSize * header.height);

	// 关闭文件
	file.close();

	// 图像二值化处理
	int threshold_value = 128; // 设定阈值


	for (int y = 0; y < header.height; y++) {
		for (int x = 0; x < header.width; x++) {
			int index = y * rowSize + x * 3;
			unsigned char r = image_data[index + 2];
			unsigned char g = image_data[index + 1];
			unsigned char b = image_data[index];

			unsigned char gray = static_cast<unsigned char>(0.299 * r + 0.587 * g + 0.114 * b);

			if (gray > threshold_value) {
				image_data[index + 2] = 255;
				image_data[index + 1] = 255;
				image_data[index] = 255;
			}
			else {
				image_data[index + 2] = 0;
				image_data[index + 1] = 0;
				image_data[index] = 0;
			}
		}
	}

	// 保存二值化后的图像数据
	ofstream output_file("binary_image.bmp", ios::binary);

	if (!output_file) {
		cout << "无法保存二值化图像文件" << endl;
		delete[] image_data;
		return -1;
	}

	std::cout << "成功保存图像文件" << std::endl;
	output_file.write(reinterpret_cast<char*>(&header), sizeof(BMPHeader));
	output_file.write(reinterpret_cast<char*>(image_data), rowSize * header.height);

	// 关闭文件
	output_file.close();

	// 释放内存
	delete[] image_data;

	return 0;
}

在上述代码中,我们使用C++的文件输入输出流来读取和保存图像文件。首先,我们读取图像的头信息,并根据宽度和高度计算图像数据的大小。然后,我们分配内存并读取彩色图像数据。接下来,我们将彩色图像转换为灰度图像,通过对每个像素的RGB值求平均来计算灰度值。最后,我们应用阈值进行二值化处理,将灰度值大于等于阈值的像素设置为白色(255),小于阈值的像素设置为黑色(0)。最后,我们保存二值化后的图像。

请注意,上述代码假设输入图像为24位位图(BMP)格式,并且图像文件名为"input.bmp"。你可以根据实际情况修改文件名和图像格式。此外,该代码只适用于处理较小的图像,如果要处理更大的图像,可能需要优化内存使用和读写操作。

(3)jpg图片转化为二值化(纯C++,没有依赖opencv相关的库)

#include <iostream>
#include <fstream>

#pragma pack(push, 1)
struct JPGHeader {
	unsigned short marker;
	unsigned short length;
};

struct SOF0Segment {
	unsigned char precision;
	unsigned short height;
	unsigned short width;
	unsigned char numComponents;
};

struct Component {
	unsigned char id;
	unsigned char samplingFactors;
	unsigned char quantizationTableId;
};

struct DQTSegment {
	unsigned char tableInfo;
	unsigned char quantizationTable[64];
};

struct SOSegment {
	unsigned char numComponents;
	Component components[3];
};

#pragma pack(pop)

bool Jpg2Binarization() 
{
	// 读取JPEG文件
	ifstream file("image.jpg", ios::binary);
	if (!file) {
		cout << "无法打开JPEG文件" << endl;
		return -1;
	}

	// 读取JPEG头信息
	JPGHeader header;
	file.read(reinterpret_cast<char*>(&header), sizeof(JPGHeader));

	if (header.marker != 0xFFD8) {
		cout << "不支持的JPEG文件格式" << endl;
		return -1;
	}

	// 找到SOF0段
	SOF0Segment sof0;
	while (true) {
		file.read(reinterpret_cast<char*>(&header), sizeof(JPGHeader));
		if (header.marker == 0xFFC0) {
			file.read(reinterpret_cast<char*>(&sof0), sizeof(SOF0Segment));
			break;
		}
		else {
			file.seekg(header.length - 2, ios::cur);
		}
	}

	// 检查图像是否为灰度图像
	if (sof0.numComponents != 1) {
		cout << "不支持的JPEG图像格式" << endl;
		return -1;
	}

	// 找到DQT段
	DQTSegment dqt;
	while (true) {
		file.read(reinterpret_cast<char*>(&header), sizeof(JPGHeader));
		if (header.marker == 0xFFDB) {
			file.read(reinterpret_cast<char*>(&dqt), sizeof(DQTSegment));
			break;
		}
		else {
			file.seekg(header.length - 2, ios::cur);
		}
	}

	// 找到SOS段
	SOSegment sos;
	while (true) {
		file.read(reinterpret_cast<char*>(&header), sizeof(JPGHeader));
		if (header.marker == 0xFFDA) {
			file.read(reinterpret_cast<char*>(&sos), sizeof(SOSegment));
			break;
		}
		else {
			file.seekg(header.length - 2, ios::cur);
		}
	}

	// 计算图像数据的宽度和高度
	int width = sof0.width;
	int height = sof0.height;

	// 创建存储图像数据的数组
	unsigned char* image_data = new unsigned char[width * height];

	// 读取图像数据
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			unsigned char value;
			file.read(reinterpret_cast<char*>(&value), sizeof(unsigned char));
			image_data[y * width + x] = value;
		}
	}

	// 关闭文件
	file.close();

	// 图像二值化处理
	int threshold_value = 128; // 设定阈值


	for (int i = 0; i < width * height; i++) {
		if (image_data[i] > threshold_value) {
			image_data[i] = 255;
		}
		else {
			image_data[i] = 0;
		}
	}

	// 保存二值化后的图像数据
	ofstream output_file("binary_image.jpg", ios::binary);
	if (!output_file) {
		cout << "无法保存二值化图像文件" << endl;
		delete[] image_data;
		return -1;
	}

	// 写入JPEG头信息
	output_file.write(reinterpret_cast<char*>(&header), sizeof(JPGHeader));
	output_file.write(reinterpret_cast<char*>(&sof0), sizeof(SOF0Segment));

	// 写入DQT段
	output_file.write(reinterpret_cast<char*>(&header), sizeof(JPGHeader));
	output_file.write(reinterpret_cast<char*>(&dqt), sizeof(DQTSegment));

	// 写入SOS段
	output_file.write(reinterpret_cast<char*>(&header), sizeof(JPGHeader));
	output_file.write(reinterpret_cast<char*>(&sos), sizeof(SOSegment));

	// 写入图像数据
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			unsigned char value = image_data[y * width + x];
			output_file.write(reinterpret_cast<char*>(&value), sizeof(unsigned char));
		}
	}

	// 关闭文件
	output_file.close();

	// 释放内存
	delete[] image_data;

	return 0;
}
(3)图片处理的一个实例:

        1)jpg通过opencv转化为二值化图片,并生成新的jpg图片;

        2)读取图片数据,重新生成jpg图片;

#include <opencv2/opencv.hpp>
#include <iostream>

void binarizeJpeg(const std::string& inputFilename, const std::string& outputFilename, int threshold)
{
    // 1、jpg图片,转为二值化图片

	// 读取JPG文件
	cv::Mat jpgImage = cv::imread(inputFilename);

	// 转换为灰度图像
	cv::Mat grayImage;
	cv::cvtColor(jpgImage, grayImage, cv::COLOR_BGR2GRAY);

	// 二值化处理
	cv::Mat binaryImage;
	cv::threshold(grayImage, binaryImage, threshold, 255, cv::THRESH_BINARY);

	// 保存为JPG文件
	cv::imwrite(outputFilename, binaryImage);



    // 2、读取jpg图片数据出来,重新生成新的图片

	unsigned char * hJpegBuf = NULL;//存储原本jpg格式的文件数据
	DWORD      JpegBufSize;
	FILE*      hfjpg;//打开原本jpg格式的文件
	errno_t err = fopen_s(&hfjpg, outputFilename.c_str(), "rb");//正确返回0,不正确返回非0
	if (err != 0)
	{
		printf("文件打开失败\n");
	}

	fseek(hfjpg, 0L, SEEK_END);
	JpegBufSize = ftell(hfjpg);
	fseek(hfjpg, 0L, SEEK_SET);
	hJpegBuf = new unsigned char[JpegBufSize];

	fread(hJpegBuf, sizeof(char), JpegBufSize, hfjpg);
	fclose(hfjpg);

	FILE* file = fopen("\\small_new.jpg").c_str(), "wb");
	fwrite(hJpegBuf, 1, JpegBufSize, file);
	fclose(file);
}



// 调用
binarizeJpeg("\\small.jpg", "\\small_Binary.jpg", 128);

  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
识别稻种个数的一般步骤如下: 1. 对待识别的图片进行预处理,包括图像增强、滤波、二值化等,以便更好地提取出稻穗的轮廓。 2. 利用图像处理技术提取稻穗的轮廓,可以使用边缘检测算法或者阈值分割算法等。 3. 对提取出来的稻穗轮廓进行形态学处理,包括腐蚀、膨胀、开闭运算等,以便更好地分离出每一个稻穗。 4. 对每一个稻穗进行特征提取,可以使用形状特征、纹理特征、颜色特征等,以便更好地区分每一个稻穗。 5. 利用机器学习算法或者深度学习算法对提取出的特征进行分类,以便识别出每一个稻穗。 下面是一个基于OpenCV库的Python代码示例,实现了对稻穗个数的识别: ```python import cv2 # 读入图像并进行预处理 img = cv2.imread('rice.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5, 5), 0) thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] # 对图像进行形态学处理,分离出每一个稻穗 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) dilate = cv2.dilate(opening, kernel, iterations=3) # 查找并绘制稻穗轮廓 cnts, _ = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in cnts: cv2.drawContours(img, [cnt], -1, (0, 255, 0), 2) # 输出稻穗个数 print('稻穗个数:', len(cnts)) # 显示结果图像 cv2.imshow('result', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 这段代码实现了对一张名为"rice.jpg"的图片进行稻穗个数的识别,主要包括预处理、形态学处理、轮廓绘制和个数输出等步骤。其,使用了高斯滤波、Otsu二值化、开运算、膨胀等图像处理技术,以及findContours函数查找轮廓,最终输出稻穗的个数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值