基于连通域和投影结合的汉字切割

本文介绍了一种基于连通域和投影算法的图像切割方法,该方法首先利用连通域找出图像中的一行文字,然后使用投影算法进一步切割出单个汉字。文章详细描述了具体的实现过程和技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include<io.h>  
#include<math.h>  
#include<vector>  
#include "cv.h"    
#include "highgui.h"    
#include "cxcore.h"    
#include <iostream>   
#include <string>  
#include "opencv2/opencv.hpp"  
#include "FreeImage.h"    
#pragma comment(lib,"FreeImage.lib")  


using namespace std;
using namespace cv;
#include <opencv2/opencv.hpp>    
int idx = 1;
double avgWidth = 0;
double avgHeight = 0;
#include <cassert>  

IplImage*  gif2ipl(const char* filename)
{
	FreeImage_Initialise();         //load the FreeImage function lib      
	FREE_IMAGE_FORMAT fif = FIF_GIF;
	FIBITMAP* fiBmp = FreeImage_Load(fif, filename, GIF_DEFAULT);
	FIMULTIBITMAP * pGIF = FreeImage_OpenMultiBitmap(fif, filename, 0, 1, 0, GIF_PLAYBACK);
	//  FIBITMAPINFO fiBmpInfo = getfiBmpInfo(fiBmp);      
	int gifImgCnt = FreeImage_GetPageCount(pGIF);
	FIBITMAP * pFrame;
	int width, height;
	width = FreeImage_GetWidth(fiBmp);
	height = FreeImage_GetHeight(fiBmp);
	IplImage * iplImg = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
	iplImg->origin = 1;//should set to 1-top-left structure(Windows bitmap style)      
	RGBQUAD* ptrPalette = new RGBQUAD; // = FreeImage_GetPalette(fiBmp);      
	BYTE intens;
	BYTE* pIntensity = &intens;
	//cvNamedWindow("gif", 0);  
	//printf("gifImgCnt %d \n", gifImgCnt);  
	for (int curFrame = 0; curFrame < gifImgCnt; curFrame++)
	{
		pFrame = FreeImage_LockPage(pGIF, curFrame);
		//ptrPalette = FreeImage_GetPalette(pFrame);      
		char * ptrImgDataPerLine;
		for (int i = 0; i < height; i++)
		{
			ptrImgDataPerLine = iplImg->imageData + i*iplImg->widthStep;
			for (int j = 0; j < width; j++)
			{
				//get the pixel index       
				//FreeImage_GetPixelIndex(pFrame,j,i,pIntensity);        
				FreeImage_GetPixelColor(pFrame, j, i, ptrPalette);
				ptrImgDataPerLine[3 * j] = ptrPalette->rgbBlue;
				ptrImgDataPerLine[3 * j + 1] = ptrPalette->rgbGreen;
				ptrImgDataPerLine[3 * j + 2] = ptrPalette->rgbRed;
				//ptrImgDataPerLine[3*j] = ptrPalette[intens].rgbBlue;      
				//ptrImgDataPerLine[3*j+1] = ptrPalette[intens].rgbGreen;      
				//ptrImgDataPerLine[3*j+2] = ptrPalette[intens].rgbRed;      
			}
		}

		//printf("convert curFrame end %d \n", curFrame);  
		//cvShowImage("gif", iplImg);  
		cvWaitKey(30);
		FreeImage_UnlockPage(pGIF, pFrame, 1);
	}
	FreeImage_Unload(fiBmp);
	FreeImage_DeInitialise();
	FreeImage_CloseMultiBitmap(pGIF, GIF_DEFAULT);
	return iplImg;
}


vector<Mat> horizontalProjectionMat(Mat srcImg)//水平投影    
{
	//cvtColor(srcImg, srcImg, CV_BGR2GRAY); // 转为灰度图像    
	Mat binImg;
	blur(srcImg, binImg, Size(3, 3));
	threshold(binImg, binImg, 0, 255, CV_THRESH_OTSU);

	int perPixelValue = 0;//每个像素的值    
	int width = srcImg.cols;
	int height = srcImg.rows;
	int* projectValArry = new int[height];//创建一个储存每行白色像素个数的数组    
	memset(projectValArry, 0, height * 4);//初始化数组    
	for (int col = 0; col < height; col++)//遍历每个像素点    
	{
		for (int row = 0; row < width; row++)
		{
			perPixelValue = binImg.at<uchar>(col, row);
			if (perPixelValue == 0)//如果是白底黑字    
			{
				projectValArry[col]++;
			}
		}
	}
	Mat horizontalProjectionMat(height, width, CV_8UC1);//创建画布    
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			perPixelValue = 255;
			horizontalProjectionMat.at<uchar>(i, j) = perPixelValue;//设置背景为白色    
		}
	}
	for (int i = 0; i < height; i++)//水平直方图    
	{
		for (int j = 0; j < projectValArry[i]; j++)
		{
			perPixelValue = 0;
			horizontalProjectionMat.at<uchar>(i, width - 1 - j) = perPixelValue;//设置直方图为黑色    
		}
	}
	//imshow("水平投影", horizontalProjectionMat);  
	vector<Mat> roiList;//用于储存分割出来的每个字符    
	int startIndex = 0;//记录进入字符区的索引    
	int endIndex = 0;//记录进入空白区域的索引    
	bool inBlock = false;//是否遍历到了字符区内    
	for (int i = 0; i <srcImg.rows; i++)
	{
		if (!inBlock && projectValArry[i] != 0)//进入字符区    
		{
			inBlock = true;
			startIndex = i;
		}
		else if (inBlock && projectValArry[i] == 0)//进入空白区    
		{
			endIndex = i;
			inBlock = false;
			Mat roiImg = srcImg(Range(startIndex, endIndex + 1), Range(0, srcImg.cols));//从原图中截取有图像的区域    
			roiList.push_back(roiImg);
		}
	}
	delete[] projectValArry;
	return roiList;
}
vector<Mat> verticalProjectionMat(Mat srcImg)//垂直投影    
{
	//cvtColor(srcImg, srcImg, CV_BGR2GRAY); // 转为灰度图像    
	Mat binImg;
	blur(srcImg, binImg, Size(3, 3));
	threshold(binImg, binImg, 0, 255, CV_THRESH_OTSU);
	int perPixelValue;//每个像素的值    
	int width = srcImg.cols;
	int height = srcImg.rows;
	int* projectValArry = new int[width];//创建用于储存每列白色像素个数的数组    
	memset(projectValArry, 0, width * 4);//初始化数组    
	for (int col = 0; col < width; col++)
	{
		for (int row = 0; row < height; row++)
		{
			perPixelValue = binImg.at<uchar>(row, col);
			if (perPixelValue == 0)//如果是白底黑字    
			{
				projectValArry[col]++;//这个数组就是统计x上一共多少个黑点。这样就形成了柱状图  
			}
		}
	}
	Mat verticalProjectionMat(height, width + 5, CV_8UC1);//垂直投影的画布    
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			perPixelValue = 255;  //背景设置为白色    
			verticalProjectionMat.at<uchar>(i, j) = perPixelValue;
		}
	}
	for (int i = 0; i < width; i++)//垂直投影直方图    
	{
		for (int j = 0; j < projectValArry[i]; j++)
		{
			perPixelValue = 0;  //直方图设置为黑色      
			verticalProjectionMat.at<uchar>(height - 1 - j, i) = perPixelValue;
		}
	}
	//imshow("垂直投影", verticalProjectionMat);  
	cvWaitKey(0);
	vector<Mat> roiList;//用于储存分割出来的每个字符    
	int startIndex = 0;//记录进入字符区的索引    
	int endIndex = 0;//记录进入空白区域的索引    
	bool inBlock = false;//是否遍历到了字符区内    
	for (int i = 0; i < srcImg.cols; i++)//cols=width    
	{
		if (!inBlock && projectValArry[i] > 0)//进入字符区    
		{
			inBlock = true;
			startIndex = i;
		}
		else if (((projectValArry[i] == 0) && inBlock) || i == srcImg.cols - 1)//进入空白区    
		{
			endIndex = i;
			inBlock = false;
			Mat roiImg = srcImg(Range(0, srcImg.rows), Range(startIndex, endIndex + 1));
			roiList.push_back(roiImg);
		}
	}
	delete[] projectValArry;
	return roiList;
}

int  qiege(char* path)
{
	//int idx = 0;  
	IplImage* imgSrc = cvLoadImage(path, CV_LOAD_IMAGE_COLOR);
	if (imgSrc == NULL){
		imgSrc = gif2ipl(path);
	}
  
  
	IplImage* img_gray = cvCreateImage(cvGetSize(imgSrc), IPL_DEPTH_8U, 1);
	cvCvtColor(imgSrc, img_gray, CV_BGR2GRAY);//转成灰色  
	IplImage* img_gray_clone = cvCreateImage(cvGetSize(imgSrc), IPL_DEPTH_8U, 1);
	cvCopy(img_gray, img_gray_clone);
	cvErode(img_gray, img_gray, NULL, 1);//腐蚀处理,并显示    

	cvAdaptiveThreshold(img_gray, img_gray, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, 21, 3);//局部阈值  
	cvAdaptiveThreshold(img_gray_clone, img_gray_clone, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, 21, 3);//局部阈值  



	CvSeq* contours = NULL;
	CvSeq* contours2 = NULL;
	CvMemStorage* storage = cvCreateMemStorage(0);
	cvFindContours(img_gray, storage, &contours, sizeof(CvContour), CV_RETR_EXTERNAL);
	cvFindContours(img_gray_clone, storage, &contours2, sizeof(CvContour), CV_RETR_EXTERNAL);
	int count1 = 0;
	int count2 = 0;
	//腐蚀后  
	for (CvSeq* c = contours; c != NULL; c = c->h_next) {
		CvRect rc = cvBoundingRect(c, 0);
		if (rc.width<10 || rc.height<10 || rc.width>500 || rc.height>500)
		{
			continue;
		}
		count1++;
	}

	//没有腐蚀  
	for (CvSeq* c = contours2; c != NULL; c = c->h_next) {
		CvRect rc = cvBoundingRect(c, 0);
		if (rc.width<10 || rc.height<10 || rc.width>500 || rc.height>500)
		{
			continue;
		}
		count2++;
	}
	cout << "count1:" << count1 << " count2:" << count2;
	if (count2 > count1){
		contours = contours2;
	}

	char szName[56] = { 0 };
	char xzName[56] = { 0 };
	avgWidth = 0;
	avgHeight = 0;
	int countAvg = 0;
	int id = 0;
	//计算均值  

	for (CvSeq* c = contours; c != NULL; c = c->h_next) {
		CvRect rc = cvBoundingRect(c, 0);
		if (rc.width<10 || rc.height<10 || rc.width>500 || rc.height>500)
		{
			continue;    // 这里可以根据轮廓的大小进行筛选  
		}
		countAvg++;
		avgWidth += rc.width;
		avgHeight += rc.height;
	}
	avgWidth = avgWidth / countAvg;
	avgHeight = avgHeight / countAvg;
	//cout << "平均宽:" << avgWidth << "平均高:" << avgHeight << endl;  
	CvSeq* q = contours;
	CvRect cr = cvBoundingRect(q, 0);
	//这个里面就相当于存了当前的矩阵  
	int max_X = 0;
	int max_Y = 0;
	for (CvSeq* cc = contours->h_next; cc != NULL; cc = cc->h_next) {
		CvRect rc = cvBoundingRect(cc, 0);
		  
		if (rc.width<avgWidth / 3 || rc.height<avgHeight / 2 || rc.width>500 || rc.height>500)
		{
			continue;    // 这里可以根据轮廓的大小进行筛选  
		}
		
		if (max_Y - rc.y - rc.height > avgHeight && (max_Y - rc.y)>avgHeight){
			cr.height = max_Y - cr.y;
			cr.width = max_X - cr.x;
			// cvDrawRect(imgSrc, cvPoint(cr.x, cr.y), cvPoint(cr.x+cr.width, cr.y+cr.height), CV_RGB(255, 0, 0));//CV_RGB(255, 0, 0)红  
			IplImage* imgNo = cvCreateImage(cvSize(cr.width, cr.height), IPL_DEPTH_8U, 3);

			cvSetImageROI(imgSrc, cr);
			//基于给定的矩形设置图像的ROI(感兴趣区域  
			//imgSrc只显示rc标识的一部分  
			cvCopy(imgSrc, imgNo);
			

			IplImage* img_ = cvCreateImage(cvGetSize(imgNo), IPL_DEPTH_8U, 1);
			cvFlip(imgNo, NULL, 0);
			cvCvtColor(imgNo, img_, CV_BGR2GRAY);
			

			Mat M = cvarrToMat(img_);
			//cvtColor(M, M, CV_BGR2GRAY); // 转为灰度图像    
			char szName[1000] = { 0 };
			vector<Mat> a = horizontalProjectionMat(M);
			if (a.size() < 1){
				a.push_back(M);
			}
			if (a.size() == 3){
				a.erase(a.begin());
				a.erase(a.begin() + 1);
			}
			 
			for (int i = 0; i < a.size(); i++)
			{
				vector<Mat> b = verticalProjectionMat(a[i]);
				
				
				for (int j = 0; j<b.size(); j++)
				{
					//getchar();  
					sprintf_s(szName, "c:/Users/asus-pc/Desktop/test/wnd_%d.jpg", idx++);
					//imshow(szName, b[j]);  
					
					resize(b[j], b[j], Size(50, 50), 0, 0, CV_INTER_LINEAR);
					imwrite(szName, b[j]);
					//直接使用b[j]不知道为啥贴不上去。只能先存为图片,再读取图片了
					Mat iii = imread(szName);
					
					
					Mat imageROI;
					Mat black = imread("c:/Users/asus-pc/Desktop/q.png");
					imageROI = black(Rect(10, 10, iii.cols, iii.rows));
					iii.copyTo(imageROI);
					IplImage imgTmp = black;
				
					IplImage *input = cvCloneImage(&imgTmp);
					IplImage* img_gray_s = cvCreateImage(cvGetSize(input), IPL_DEPTH_8U, 1);
					cvCvtColor(input, img_gray_s, CV_BGR2GRAY);
					
					
					cvAdaptiveThreshold(img_gray_s, img_gray_s, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, 21, 3);//局部阈值
					
					cvSaveImage(szName, img_gray_s);//保存切分的结果   

					cvReleaseImage(&input);
					cvReleaseImage(&imgNo);
				}
			}

			

			cvResetImageROI(imgSrc);

			
			cr = rc;
			max_X = rc.x + rc.width;
			max_Y = rc.y + rc.height;
			cvReleaseImage(&imgNo);
			//到投影处理  

		}


		else{
			//如果在一行或者一块就进行合并,再处理 cr外,rc内  
			if (cr.x > rc.x){
				cr.x = rc.x;//记录最高点  
			}


			if (cr.y > rc.y){
				cr.y = rc.y;//记录最高点  
			}
			if (rc.x + rc.width > max_X){
				max_X = rc.x + rc.width;//记录最低点  
			}

			if (rc.y + rc.height>max_Y){
				max_Y = rc.y + rc.height;//记录最低点  
			}
		}
		
	}
	cvFlip(imgSrc, NULL, 0);
	Mat T = cvarrToMat(imgSrc);
	resize(T, T, Size(900, 900), 0, 0, CV_INTER_LINEAR);
	imshow("name", T);


	cvShowImage("原图", imgSrc);//显示原图  
	cvWaitKey(0);
	cvReleaseMemStorage(&storage);
	cvReleaseImage(&imgSrc); //cvReleaseImage函数只是将IplImage*型的变量值赋为NULL,  
	// 而这个变量本身还是存在的并且在内存中的存储位置不变。  
	cvReleaseImage(&img_gray);
	cvReleaseImage(&img_gray_clone);
	//  
	cvDestroyAllWindows();
	//销毁所有HighGUI窗口  
	cvWaitKey(0);
	return 0;
}


int main()
{//"c:/Users/asus-pc/Desktop/1234.jpg"C:\Users\as、s-pc\Desktop\jpg-t  

	//要切割图的存储路径
	string path = "c:\\Users\\asus-pc\\Desktop\\11";
	intptr_t hFile;
	vector<string> files;
	//文件信息     struct _finddata_t fileinfo;  
	struct _finddata_t fileinfo;  //很少用的文件信息读取结构  
	string p;
	char px[100] = { 0 };
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
		do {
			if ((fileinfo.attrib & _A_SUBDIR)) {

			}
			else {
				//files.push_back(fileinfo.name);  
				//files.push_back(p.assign(path).append("\\").append(fileinfo.name));  
				sprintf_s(px, "c:/Users/asus-pc/Desktop/11/%s", fileinfo.name);
				cout << fileinfo.name << endl;
				qiege(px);
			}
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
	getchar();
	return 0;

}

 


 

 

 

这段代码,先使用基于连通域的切割找出一行的,再通过基于投影的算法切割出每个汉字。当然还有需要优化的地方。对图片不是很清晰的,清晰还得调整腐蚀次数等等,做的还是不是很完善,供大家参考,代码也比较乱。有些是网上找的代码自己进行了加工。

 

 

 

 

 

 

 

 

 

 

 

<think>好的,我需要帮助用户使用OpenCV实现汉字日文的OCR字符识别。首先,用户的问题涉及汉字日文,这两者的字符结构比英文字母更复杂,所以传统的OCR方法可能不够准确。不过用户提到了参考引用中的内容,特别是引用[2]提到的OCR身份证识别流程,可能需要借鉴其中的步骤。 首先,我应该回顾OpenCVOCR中的常规流程,比如图像预处理、文本检测、字符分割字符识别。对于汉字日文来说,字符种类多,结构复杂,可能需要更精细的图像处理更强大的分类模型。传统的模板匹配或者简单的特征提取(如HOG、SIFT)可能效果有限,尤其是在字符变形或字体变化的情况下。 接下来,用户提供的引用[1]提到可以处理图像的不同输入形式,比如numpy数组、字节文件或URL,这需要在代码中支持多种输入方式。但用户的主要问题是如何实现OCR,特别是针对复杂字符。 然后,我需要考虑使用OpenCV结合机器学习或深度学习模型。例如,使用OpenCV进行预处理,然后利用训练好的模型进行识别。引用[2]中提到的实战项目可能包含相关案例,比如身份证识别,这通常涉及汉字识别,可以借鉴其中的方法。 步骤可能包括: 1. 图像预处理:灰度化、二值化、去噪、校正等。使用OpenCV的函数如cv2.cvtColor、cv2.threshold、高斯模糊、形态学操作等。 2. 文本区域检测:可能需要使用轮廓检测(cv2.findContours)、边缘检测(Canny)或者基于深度学习的文本检测模型(如EAST)。 3. 字符分割:对于汉字日文,字符通常是等距排列,可能需要投影分析或者连通区域分析来分割单个字符。 4. 特征提取:传统方法如HOG、SIFT,或者使用深度学习特征。但考虑到字符复杂性,深度学习可能更有效。 5. 字符分类:训练一个分类模型,如SVM、随机森林,或者使用CNN。由于字符类别多(汉字日文可能有数千个),需要较大的数据集较深的网络。 但是,用户可能没有现成的模型,所以需要考虑如何训练或获取预训练模型。引用[2]提到的OpenCV实战项目可能提供了一些预训练模型或者数据集,特别是身份证识别,但日文可能需要另外的数据集。 此外,OpenCV本身可能不足以处理复杂的OCR任务,尤其是对于中文日文,可能需要结合其他库,如Tesseract,但用户明确要求使用OpenCV实现。因此,可能需要自己构建一个流程,或者利用OpenCV的DNN模块加载预训练的深度学习模型,如CRNN或基于CNN的模型。 可能的解决方案步骤: - 使用OpenCV进行图像预处理。 - 使用EAST模型进行文本检测(OpenCV的DNN支持)。 - 分割出文本行,然后进一步分割字符。 - 使用训练好的CNN模型进行字符识别,模型需支持汉字日文字符。 但是训练这样的模型需要大量标注数据,对于普通用户来说可能困难。因此,可能需要建议用户使用现有的OCR引擎,如Tesseract,并训练其语言模型,或者结合OpenCV进行预处理后调用这些引擎。不过用户坚持使用OpenCV,可能需要寻找OpenCV与深度学习模型结合的方法。 总结思路: 1. 预处理图像,增强文本区域。 2. 检测文本区域并分割字符。 3. 使用深度学习模型识别字符,模型需支持多语言。 4. 可能需要用户自行训练模型或使用预训练模型。 在代码示例中,可以展示预处理轮廓检测的部分,但字符识别部分可能需要依赖训练好的模型文件。例如,使用OpenCV的dnn.readNetFromTensorflow加载一个训练好的CNN模型。 另外,用户的问题中提到汉字日文,需要注意字符编码模型的支持范围。比如,模型需要覆盖所有需要的字符类别,这可能需要非常大的分类层。 最后,生成相关问题时要围绕实现步骤、优化方法、数据集准备等。</think>使用OpenCV实现汉字与日文OCR字符识别需要结合传统图像处理技术机器学习方法,下面是分步实现方案: ### 一、核心步骤 1. **图像预处理** - 灰度化:$cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)$ - 二值化:$cv2.threshold(gray, 127, 255, cv2.THRESH_OTSU)$ - 去噪:$cv2.fastNlMeansDenoising()$ - 文本区域增强:直方图均衡化或CLAHE算法[^1] 2. **文本检测与定位** - 使用轮廓检测:$contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)$ - 或使用深度学习模型(如EAST文本检测器):需加载预训练的.pb模型文件[^2] 3. **字符分割** - 投影分析:垂直投影切割单个字符 - 连通域分析:$cv2.connectedComponentsWithStats()$ ```python # 示例:垂直投影分割 vertical_projection = np.sum(binary_img, axis=0) char_positions = np.where(vertical_projection > threshold)[0] ``` 4. **特征提取与识别** - 传统方法:提取HOG特征 + SVM分类器 - 深度学习方法: ```python # 加载预训练CNN模型 net = cv2.dnn.readNetFromTensorflow("ocr_model.pb") blob = cv2.dnn.blobFromImage(char_img, scalefactor=1/127.5, size=(32,32)) net.setInput(blob) preds = net.forward() ``` ### 二、关键问题解决方案 1. **字符集覆盖** 汉字日文联合字符集超过2万个,需使用支持大类别分类的深度学习模型。建议采用CRNN(卷积循环神经网络)结构,参考身份证识别中的汉字处理方法[^2]。 2. **训练数据准备** - 中文数据集:CASIA-HWDB手写数据库 - 日文数据集:ETL字符数据库 - 数据增强:旋转(±10°)、平移、弹性变形 ### 三、完整代码示例 ```python import cv2 import numpy as np def ocr_jp_cn(image): # 预处理 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV) # 文本检测(示例使用轮廓法) contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 加载预训练模型 net = cv2.dnn.readNetFromTensorflow("crnn_model.pb") results = [] for cnt in contours: x,y,w,h = cv2.boundingRect(cnt) char_img = binary[y:y+h, x:x+w] # 输入标准化 resized = cv2.resize(char_img, (32,32)) blob = cv2.dnn.blobFromImage(resized, scalefactor=1/127.5) # 推理 net.setInput(blob) preds = net.forward() char_code = np.argmax(preds) results.append(decode_char(char_code)) # 自定义解码函数 return "".join(results) ``` ### 四、优化建议 1. **模型选择** 优先选用CRNN等支持序列识别的模型,避免单个字符切割不准确的问题[^2] 2. **多语言支持** 合并中文字符集(GB2312)日文JIS标准字符集,需重新训练分类层 3. **性能提升** - 使用OpenCL加速:$cv2.ocl.setUseOpenCL(True)$ - 批量推理:将多个字符图像合并为一个batch输入
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值