OneOCR — скрытая OCR внутри Windows 11. ml.. ml. ocr.
OneOCR — скрытая OCR внутри Windows 11 - 1

OneOCR — это набор из двух динамических библиотек и одной модели ONNX для распознавания текста в приложениях Snipping Tool и Photos в Windows 11.


Скажу сразу: статьи писать я не умею, а воды лить не хочу, поэтому писанины будет немного.

Итак, набор из трёх файлов состоит из: oneocr.dll, onnxruntime.dll и oneocr.onemodel.

Microsoft официально не предоставляет документацию и API к библиотеке OneOCR. Первое упоминание о OneOCR встречается у пользователя github b1tg. Все остальные найденные источники ссылаются на него.

Не знаю точно в какой версии Windows появилась эта функция, но в 23H2 у Snipping Tool 11.2409.25.0 она уже была. Также эти библиотеки и ONNX модель есть и у приложения Photos.

На момент написания статьи у меня в Windows 11 Pro 24H2 (26100.8328) установлены версии Photos 2026.11020.20001.0 и Snipping Tool 11.2601.12.0. При чём версия onnxruntime.dll у Snipping Tool старее (1.19.0.0) чем в Photos (1.23.0.0). Модели также различаются по содержимомоу.

Для примера я буду использовать ту, что посвежее. Замечу, что OneOCR хорошо работает на CPU (даже на моём стареньком Intel Core i3-4170), хотя, судя по релизам onnxruntime и экспортируемой из oneocr.dll функции OcrProcessOptionsSetRunBackendModelOnCPU, может работать и на GPU, но, скажу сразу, задействовать GPU мне не удалось.

Для минимального примера понадобится импортировать всего 5 функций:

result_t CreateOcrPipeline( const char *, const char *, init_options_t, pipeline_t * );
result_t RunOcrPipeline( pipeline_t, const image_t *, process_options_t, instance_t * );
result_t GetOcrLineCount( instance_t, uint64_t * );
result_t GetOcrLine( instance_t, uint64_t, line_desc_t * );
result_t GetOcrLineContent( line_desc_t, const char ** );

result_t — просто 32-битный целочисленный тип, хранящий код ошибки (всего их может быть 8, значения от 0 до 7, где 0 — это успешный результат).

init_options_t, process_options_t, pipeline_t, instance_t и line_desc_t — это указатели на внутренние структуры. Они «непрозрачны» и их содержимое по большому счету неизвестно, да нам оно и не особо-то и интересно, на самом деле.

image_t — структура, хранящая информацию об изображении. Информация о двух полях в этой структуре пока остаётся загадкой, но известно, какие значения они могут принимать и для примера этого хватит. Эксперименты показывают, что изображение должно быть в формате 32bpp (4 байта на пиксель). Причём порядок каналов RGB, как я понял, значения не имеет (ведь какая для ML-модели разница, какой будет текст, синий или красный?).

Простейший пример для лучшего понимания (я намеренно убрал все проверки на ошибки и код освобождения ресурсов):

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincodec.h>
#include <cstdio>
#include <cstdint>

struct image_t {
	uint32_t	type;		// 0, 1, 2, or 3
	uint32_t	width;
	uint32_t	height;
	uint32_t	reserved;	// ???
	uint64_t	stride;
	uint8_t *	data;
}; // struct image_t

using result_t			= uint32_t;
using init_options_t	= void *;
using process_options_t	= void *;
using pipeline_t		= void *;
using instance_t		= void *;
using line_desc_t		= void *;

result_t (__cdecl *CreateOcrPipeline)( const char *, const char *, init_options_t, pipeline_t * );
result_t (__cdecl *RunOcrPipeline)( pipeline_t, const image_t *, process_options_t, instance_t * );
result_t (__cdecl *GetOcrLineCount)( instance_t, uint64_t * );
result_t (__cdecl *GetOcrLine)( instance_t, uint64_t, line_desc_t * );
result_t (__cdecl *GetOcrLineContent)( line_desc_t, const char ** );
void     (__cdecl *ReleaseOcrPipeline)( pipeline_t );

void load_image( const wchar_t * filename, image_t * p_image ) {
	IWICImagingFactory *	p_Factory		= NULL;
	IWICBitmapDecoder *		p_Decoder		= NULL;
	IWICBitmapFrameDecode *	p_Frame			= NULL;
	IWICBitmapSource *		p_BitmapSource	= NULL;
	IWICFormatConverter *	p_Converter		= NULL;
	IWICBitmapSource *		p_Result		= NULL;

	CoInitialize( NULL );
	CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS( &p_Factory ) );
	p_Factory->CreateDecoderFromFilename( filename, NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &p_Decoder );
	p_Decoder->GetFrame( 0, &p_Frame );
	p_Frame->QueryInterface( IID_IWICBitmapSource, reinterpret_cast<void **>(&p_BitmapSource) );

	p_Frame->GetSize( &p_image->width, &p_image->height );
	p_image->stride	= p_image->width * 4;
	p_image->data	= new uint8_t [p_image->stride * p_image->height];

	WICPixelFormatGUID pixel_format;
	p_Frame->GetPixelFormat( &pixel_format );

	// Convert format to GUID_WICPixelFormat32bppRGB...
	if ( pixel_format != GUID_WICPixelFormat32bppRGB ) {
		p_Factory->CreateFormatConverter( &p_Converter );
		p_Converter->Initialize( p_BitmapSource, GUID_WICPixelFormat32bppBGR, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeCustom );
		p_Converter->QueryInterface( IID_PPV_ARGS( &p_Result ) );
	}

	p_Result->CopyPixels( NULL, p_image->stride, p_image->stride * p_image->height, reinterpret_cast<BYTE *>( p_image->data ) );
}

int main( int argc, char * args[] ) {
	SetConsoleOutputCP( CP_UTF8 );

	if ( argc != 2 ) {
		printf( "Use: %s image_filenamen", args[0] );
		return 1;
	}

	HMODULE	dll = LoadLibrary( "oneocr.dll" );

	CreateOcrPipeline	= (decltype( CreateOcrPipeline ))GetProcAddress( dll, "CreateOcrPipeline" );
	RunOcrPipeline		= (decltype( RunOcrPipeline    ))GetProcAddress( dll, "RunOcrPipeline" );
	GetOcrLineCount		= (decltype( GetOcrLineCount   ))GetProcAddress( dll, "GetOcrLineCount" );
	GetOcrLine			= (decltype( GetOcrLine        ))GetProcAddress( dll, "GetOcrLine" );
	GetOcrLineContent	= (decltype( GetOcrLineContent ))GetProcAddress( dll, "GetOcrLineContent" );

	// Convert image path to wide-char for image loader...
	wchar_t	w_filename[MAX_PATH] = {};
	swprintf( w_filename, MAX_PATH, L"%s", args[1] );

	image_t image = { 3/*type*/ };
	load_image( w_filename, &image );

	pipeline_t pipeline = nullptr;
	CreateOcrPipeline( "oneocr.onemodel", "kj)TGtrK>f]b[Piow.gU+nC@s""""""4", nullptr/*init options*/, &pipeline );

	instance_t instance = nullptr;
	RunOcrPipeline( pipeline, &image, nullptr/*process options*/, &instance );

	uint64_t n_lines = 0;
	GetOcrLineCount( instance, &n_lines );

	for ( int i = 0; i < n_lines; i++ ) {

		// Get line descriptor...
		line_desc_t line_desc = nullptr;
		GetOcrLine( instance, i, &line_desc );
		if ( line_desc ) {

			// Get null-terminated line ptr...
			const char * line_ptr = nullptr;
			GetOcrLineContent( line_desc, &line_ptr );
			printf( " %sn", line_ptr );
		}

	}
	return 0;
}

Код элементарный, комментировать тут особо нечего.

Пара примеров распознавания рукописного текста (я человек простой, поэтому примеры изображений стащил из чужой статьи 2025-го года):

Скрытый текст
OneOCR — скрытая OCR внутри Windows 11 - 2
 Привет. Это образен для распознавания
 ТЕкстА ДЛя сТАТьИ в Т-Ж. ПровЕрим
 сколько слов он определит полностью,
 >
 сколько прЕврАтит в нАБор Букв и
 сколько вообш,Е нЕ узнаЕт:
 1. Набор слов для распознавания №1.
 2. Ещё один набор слов.
 3. Третий набор слово.
 4. Экспрессия-четвертое слово.
 5. Финальное пятое слово.
Скрытый текст
OneOCR — скрытая OCR внутри Windows 11 - 3
 Я вас любил: любовь ещё, быть может
 В душе моей учасла не совсем;
 пусть она вас большше не тревожит;
 Я не хочу печалить вас ничим.
 Я вас любии безлитивно, безна дежно,
 По рабостью, то равностью тамим;
 Я вас любил этал искренно, ток нежно,
 Как дай вам Бы любимой быть другим.

Ну и один пример распознавания печатного текста (изображение было взято где-то в сети интернет):

Скрытый текст
OneOCR — скрытая OCR внутри Windows 11 - 4
 В богатовских садах
 Рассказ
 М.С.Богатовец
 Начиная, от Панфилихи по обрывистому берегу Северного Донца в
 протяжении двух километров тянутся богатовские сады. Это одно из бо-
 гатых мест хутора. Оно всегда привлекало на себя внимание не только
 народа, но и перелетную птицу, полевых зверей и охотников на чужое
 добро. В летнее время по садам кочуют косяками дворовые подростки.
 Они охотятся на сорочиные яйца, касачек, сизокрылых шнурков. Купают-
 ся в Донце и исподволь пасутся по чужому крыжовнику. Свежий ветерок
 из Донца покачивает богатые деревья фруктами: яблоками, сливами, гру-
 шами и обильной вишней.
 ***
 В истории прошлого, участки садов передавались из рук в руки по на-
 следству. Местные жители хорошо возделывают землю под садами. На
 зиму или весной земля вскапывается. Нижние части плодородных ство-
 лов наносятся известью, для борьбы с вредителями. А сухие деревья
 уничтожаются и используются для топлива. Стены садов слегка ремонти-
 руются, как ограждение от бродящего скота.
 Сады всячески охраняются, особенно в весеннее время. Осенью, ког-
 да все с садов убрано, в ночное время некоторые жители позволяют себе
 и запускают скот для откормки. Однако, это в ущерб садам, и в этот пери-
 од некоторые более трудолюбивые хозяева усиливают ограждение. Над-
 сматривают над своим салом и порыкивают на свиней и скотину.
 ***
 В особенности привлекательны сады в весеннее время, когда зелень
 в полном своем расцвете. С садов доносится в хутор несмолкаемый пти-
 чий гам. Поют, где-то в поднебесьях соловьи. Пророчат вечно перелетные
 кукушки. Пыхтят на каменных стенах пастушки со своим острым гребнем.
 Летают и пересаживаются из стены на стену чикалки, помахивая сво-
 им сереньким хвостом. Пролетают над головами, на большой скорости
 скворцы. Они направляют свой путь в колки, где им привольно и обилье
 корма. Воркуют горлинки по своим детям. Эта одна из бедных несушек
 в наших садах, которая не в состоянии даже сделать, как следует себе
 гнездышко. Пролетают над садами дикие утки. Их полет напоминает ле-
 тевшую со свистом бомбу. Где-то далеко в садах назойливо поет иволга.
 Летят сороки по направлению хутора на охоту на цыплят. Ее небольшие
 крылья стягивают к себе воздух. Сзади широкий разлатый хвост, регули-
 рует свое направление. Черные галки сидят на вершинах тополей и ос-
 матривают окружение. По Дону, шлепая полостями, поднимается в гору
 пароход с баржами, и выпущенным черным дымом покрывает сады.
 ***
 Пестреющий в садах народ, как и птица, каждый занят своим делом.
 Одни полют высоко поднявшуюся траву. Другие, закутав нос платком от
 назойливых комаров, рвут вишню завтра к базару. Третьи чинят изгородь,
 рвут калюку и накладывают на стену. Четвертые, расположившись под

Пример выше и более правильная реализация (с проверками ошибок и т. п.) находятся на GitHub

Ссылки на первоисточники

Автор: AnSa8

Источник

  • Запись добавлена: 06.05.2026 в 13:29
  • Оставлено в