- BrainTools - https://www.braintools.ru -

OneOCR — это набор из двух динамических библиотек и одной модели ONNX для распознавания текста в приложениях Snipping Tool и Photos в Windows 11.
Скажу сразу: статьи писать я не умею, а воды лить не хочу, поэтому писанины будет немного.
Итак, набор из трёх файлов состоит из: oneocr.dll, onnxruntime.dll и oneocr.onemodel.
Microsoft официально не предоставляет документацию и API к библиотеке OneOCR. Первое упоминание о OneOCR встречается у пользователя github b1tg [1]. Все остальные найденные источники ссылаются на него.
Не знаю точно в какой версии 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 [2] и экспортируемой из 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-битный целочисленный тип, хранящий код ошибки [3] (всего их может быть 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;
}
Код элементарный, комментировать тут особо нечего.
Пара примеров распознавания рукописного текста (я человек простой, поэтому примеры изображений стащил из чужой статьи [4] 2025-го года):

Привет. Это образен для распознавания
ТЕкстА ДЛя сТАТьИ в Т-Ж. ПровЕрим
сколько слов он определит полностью,
>
сколько прЕврАтит в нАБор Букв и
сколько вообш,Е нЕ узнаЕт:
1. Набор слов для распознавания №1.
2. Ещё один набор слов.
3. Третий набор слово.
4. Экспрессия-четвертое слово.
5. Финальное пятое слово.

Я вас любил: любовь ещё, быть может
В душе моей учасла не совсем;
пусть она вас большше не тревожит;
Я не хочу печалить вас ничим.
Я вас любии безлитивно, безна дежно,
По рабостью, то равностью тамим;
Я вас любил этал искренно, ток нежно,
Как дай вам Бы любимой быть другим.
Ну и один пример распознавания печатного текста (изображение было взято где-то в сети интернет):

В богатовских садах
Рассказ
М.С.Богатовец
Начиная, от Панфилихи по обрывистому берегу Северного Донца в
протяжении двух километров тянутся богатовские сады. Это одно из бо-
гатых мест хутора. Оно всегда привлекало на себя внимание не только
народа, но и перелетную птицу, полевых зверей и охотников на чужое
добро. В летнее время по садам кочуют косяками дворовые подростки.
Они охотятся на сорочиные яйца, касачек, сизокрылых шнурков. Купают-
ся в Донце и исподволь пасутся по чужому крыжовнику. Свежий ветерок
из Донца покачивает богатые деревья фруктами: яблоками, сливами, гру-
шами и обильной вишней.
***
В истории прошлого, участки садов передавались из рук в руки по на-
следству. Местные жители хорошо возделывают землю под садами. На
зиму или весной земля вскапывается. Нижние части плодородных ство-
лов наносятся известью, для борьбы с вредителями. А сухие деревья
уничтожаются и используются для топлива. Стены садов слегка ремонти-
руются, как ограждение от бродящего скота.
Сады всячески охраняются, особенно в весеннее время. Осенью, ког-
да все с садов убрано, в ночное время некоторые жители позволяют себе
и запускают скот для откормки. Однако, это в ущерб садам, и в этот пери-
од некоторые более трудолюбивые хозяева усиливают ограждение. Над-
сматривают над своим салом и порыкивают на свиней и скотину.
***
В особенности привлекательны сады в весеннее время, когда зелень
в полном своем расцвете. С садов доносится в хутор несмолкаемый пти-
чий гам. Поют, где-то в поднебесьях соловьи. Пророчат вечно перелетные
кукушки. Пыхтят на каменных стенах пастушки со своим острым гребнем.
Летают и пересаживаются из стены на стену чикалки, помахивая сво-
им сереньким хвостом. Пролетают над головами, на большой скорости
скворцы. Они направляют свой путь в колки, где им привольно и обилье
корма. Воркуют горлинки по своим детям. Эта одна из бедных несушек
в наших садах, которая не в состоянии даже сделать, как следует себе
гнездышко. Пролетают над садами дикие утки. Их полет напоминает ле-
тевшую со свистом бомбу. Где-то далеко в садах назойливо поет иволга.
Летят сороки по направлению хутора на охоту на цыплят. Ее небольшие
крылья стягивают к себе воздух. Сзади широкий разлатый хвост, регули-
рует свое направление. Черные галки сидят на вершинах тополей и ос-
матривают окружение. По Дону, шлепая полостями, поднимается в гору
пароход с баржами, и выпущенным черным дымом покрывает сады.
***
Пестреющий в садах народ, как и птица, каждый занят своим делом.
Одни полют высоко поднявшуюся траву. Другие, закутав нос платком от
назойливых комаров, рвут вишню завтра к базару. Третьи чинят изгородь,
рвут калюку и накладывают на стену. Четвертые, расположившись под
Пример выше и более правильная реализация (с проверками ошибок и т. п.) находятся на GitHub [5]
Автор: AnSa8
Источник [7]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/29912
URLs in this post:
[1] b1tg: https://b1tg.github.io/post/win11-oneocr
[2] onnxruntime: https://github.com/microsoft/onnxruntime/releases
[3] ошибки: http://www.braintools.ru/article/4192
[4] статьи: https://habr.com/ru/articles/895664/
[5] GitHub: https://github.com/AnSa8x/OneOCR
[6] https://github.com/b1tg/win11-oneocr: https://github.com/b1tg/win11-oneocr
[7] Источник: https://habr.com/ru/articles/1032188/?utm_campaign=1032188&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.