Skip to content

Commit 6f8f085

Browse files
authored
refactor: add a shared base class for OCR controllers (#756)
## Description Current implementation of the OCR controller looks nearly identical, introducing a lot of code repetition. This PR creates a base class so the logic can be shared between vertical and standard ocr controllers. ### Introduces a breaking change? - [ ] Yes - [ ] No ### Type of change - [ ] Bug fix (change which fixes an issue) - [ ] New feature (change which adds functionality) - [ ] Documentation update (improves or adds clarity to existing documentation) - [ ] Other (chores, tests, code style improvements etc.) ### Tested on - [ ] iOS - [ ] Android ### Testing instructions <!-- Provide step-by-step instructions on how to test your changes. Include setup details if necessary. --> ### Screenshots <!-- Add screenshots here, if applicable --> ### Related issues <!-- Link related issues here using #issue-number --> ### Checklist - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have updated the documentation accordingly - [ ] My changes generate no new warnings ### Additional notes <!-- Include any additional information, assumptions, or context that reviewers might need to understand this PR. -->
1 parent 2d18c89 commit 6f8f085

3 files changed

Lines changed: 157 additions & 199 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { symbols } from '../constants/ocr/symbols';
2+
import { RnExecutorchErrorCode } from '../errors/ErrorCodes';
3+
import { RnExecutorchError, parseUnknownError } from '../errors/errorUtils';
4+
import { ResourceSource } from '../types/common';
5+
import { OCRLanguage, OCRDetection } from '../types/ocr';
6+
import { ResourceFetcher } from '../utils/ResourceFetcher';
7+
8+
export abstract class BaseOCRController {
9+
protected nativeModule: any;
10+
public isReady: boolean = false;
11+
public isGenerating: boolean = false;
12+
public error: RnExecutorchError | null = null;
13+
protected isReadyCallback: (isReady: boolean) => void;
14+
protected isGeneratingCallback: (isGenerating: boolean) => void;
15+
protected errorCallback: (error: RnExecutorchError) => void;
16+
17+
constructor({
18+
isReadyCallback = (_isReady: boolean) => {},
19+
isGeneratingCallback = (_isGenerating: boolean) => {},
20+
errorCallback = (_error: RnExecutorchError) => {},
21+
} = {}) {
22+
this.isReadyCallback = isReadyCallback;
23+
this.isGeneratingCallback = isGeneratingCallback;
24+
this.errorCallback = errorCallback;
25+
}
26+
27+
protected abstract loadNativeModule(
28+
detectorPath: string,
29+
recognizerPath: string,
30+
language: OCRLanguage,
31+
extraParams?: any
32+
): any;
33+
34+
protected internalLoad = async (
35+
detectorSource: ResourceSource,
36+
recognizerSource: ResourceSource,
37+
language: OCRLanguage,
38+
onDownloadProgressCallback?: (downloadProgress: number) => void,
39+
extraParams?: any
40+
) => {
41+
try {
42+
if (!detectorSource || !recognizerSource) return;
43+
44+
if (!symbols[language]) {
45+
throw new RnExecutorchError(
46+
RnExecutorchErrorCode.LanguageNotSupported,
47+
'The provided language for OCR is not supported. Please try using other language.'
48+
);
49+
}
50+
51+
this.isReady = false;
52+
this.isReadyCallback(false);
53+
54+
const paths = await ResourceFetcher.fetch(
55+
onDownloadProgressCallback,
56+
detectorSource,
57+
recognizerSource
58+
);
59+
if (paths === null || paths.length < 2) {
60+
throw new RnExecutorchError(
61+
RnExecutorchErrorCode.DownloadInterrupted,
62+
'The download has been interrupted. As a result, not every file was downloaded. Please retry the download.'
63+
);
64+
}
65+
this.nativeModule = this.loadNativeModule(
66+
paths[0]!,
67+
paths[1]!,
68+
language,
69+
extraParams
70+
);
71+
this.isReady = true;
72+
this.isReadyCallback(this.isReady);
73+
} catch (e) {
74+
if (this.errorCallback) {
75+
this.errorCallback(parseUnknownError(e));
76+
} else {
77+
throw parseUnknownError(e);
78+
}
79+
}
80+
};
81+
82+
public forward = async (imageSource: string): Promise<OCRDetection[]> => {
83+
if (!this.isReady) {
84+
throw new RnExecutorchError(
85+
RnExecutorchErrorCode.ModuleNotLoaded,
86+
'The model is currently not loaded. Please load the model before calling forward().'
87+
);
88+
}
89+
if (this.isGenerating) {
90+
throw new RnExecutorchError(
91+
RnExecutorchErrorCode.ModelGenerating,
92+
'The model is currently generating. Please wait until previous model run is complete.'
93+
);
94+
}
95+
96+
try {
97+
this.isGenerating = true;
98+
this.isGeneratingCallback(this.isGenerating);
99+
return await this.nativeModule.generate(imageSource);
100+
} catch (e) {
101+
throw parseUnknownError(e);
102+
} finally {
103+
this.isGenerating = false;
104+
this.isGeneratingCallback(this.isGenerating);
105+
}
106+
};
107+
108+
public delete() {
109+
if (this.isGenerating) {
110+
throw new RnExecutorchError(
111+
RnExecutorchErrorCode.ModelGenerating,
112+
'The model is currently generating. Please wait until previous model run is complete.'
113+
);
114+
}
115+
if (this.nativeModule) {
116+
this.nativeModule.unload();
117+
}
118+
this.isReadyCallback(false);
119+
this.isGeneratingCallback(false);
120+
}
121+
}
Lines changed: 15 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,15 @@
11
import { symbols } from '../constants/ocr/symbols';
2-
import { RnExecutorchErrorCode } from '../errors/ErrorCodes';
3-
import { RnExecutorchError, parseUnknownError } from '../errors/errorUtils';
42
import { ResourceSource } from '../types/common';
53
import { OCRLanguage } from '../types/ocr';
6-
import { ResourceFetcher } from '../utils/ResourceFetcher';
7-
8-
export class OCRController {
9-
private nativeModule: any;
10-
public isReady: boolean = false;
11-
public isGenerating: boolean = false;
12-
public error: RnExecutorchError | null = null;
13-
private isReadyCallback: (isReady: boolean) => void;
14-
private isGeneratingCallback: (isGenerating: boolean) => void;
15-
private errorCallback: (error: RnExecutorchError) => void;
16-
17-
constructor({
18-
isReadyCallback = (_isReady: boolean) => {},
19-
isGeneratingCallback = (_isGenerating: boolean) => {},
20-
errorCallback = (_error: RnExecutorchError) => {},
21-
} = {}) {
22-
this.isReadyCallback = isReadyCallback;
23-
this.isGeneratingCallback = isGeneratingCallback;
24-
this.errorCallback = errorCallback;
4+
import { BaseOCRController } from './BaseOCRController';
5+
6+
export class OCRController extends BaseOCRController {
7+
protected loadNativeModule(
8+
detectorPath: string,
9+
recognizerPath: string,
10+
language: OCRLanguage
11+
): any {
12+
return global.loadOCR(detectorPath, recognizerPath, symbols[language]);
2513
}
2614

2715
public load = async (
@@ -30,83 +18,11 @@ export class OCRController {
3018
language: OCRLanguage,
3119
onDownloadProgressCallback?: (downloadProgress: number) => void
3220
) => {
33-
try {
34-
if (!detectorSource || !recognizerSource) return;
35-
36-
if (!symbols[language]) {
37-
throw new RnExecutorchError(
38-
RnExecutorchErrorCode.LanguageNotSupported,
39-
'The provided language for OCR is not supported. Please try using other language.'
40-
);
41-
}
42-
43-
this.isReady = false;
44-
this.isReadyCallback(false);
45-
46-
const paths = await ResourceFetcher.fetch(
47-
onDownloadProgressCallback,
48-
detectorSource,
49-
recognizerSource
50-
);
51-
if (paths === null || paths.length < 2) {
52-
throw new RnExecutorchError(
53-
RnExecutorchErrorCode.DownloadInterrupted,
54-
'The download has been interrupted. As a result, not every file was downloaded. Please retry the download.'
55-
);
56-
}
57-
this.nativeModule = global.loadOCR(
58-
paths[0]!,
59-
paths[1]!,
60-
symbols[language]
61-
);
62-
this.isReady = true;
63-
this.isReadyCallback(this.isReady);
64-
} catch (e) {
65-
if (this.errorCallback) {
66-
this.errorCallback(parseUnknownError(e));
67-
} else {
68-
throw parseUnknownError(e);
69-
}
70-
}
21+
await this.internalLoad(
22+
detectorSource,
23+
recognizerSource,
24+
language,
25+
onDownloadProgressCallback
26+
);
7127
};
72-
73-
public forward = async (imageSource: string) => {
74-
if (!this.isReady) {
75-
throw new RnExecutorchError(
76-
RnExecutorchErrorCode.ModuleNotLoaded,
77-
'The model is currently not loaded. Please load the model before calling forward().'
78-
);
79-
}
80-
if (this.isGenerating) {
81-
throw new RnExecutorchError(
82-
RnExecutorchErrorCode.ModelGenerating,
83-
'The model is currently generating. Please wait until previous model run is complete.'
84-
);
85-
}
86-
87-
try {
88-
this.isGenerating = true;
89-
this.isGeneratingCallback(this.isGenerating);
90-
return await this.nativeModule.generate(imageSource);
91-
} catch (e) {
92-
throw parseUnknownError(e);
93-
} finally {
94-
this.isGenerating = false;
95-
this.isGeneratingCallback(this.isGenerating);
96-
}
97-
};
98-
99-
public delete() {
100-
if (this.isGenerating) {
101-
throw new RnExecutorchError(
102-
RnExecutorchErrorCode.ModelGenerating,
103-
'The model is currently generating. Please wait until previous model run is complete.'
104-
);
105-
}
106-
if (this.nativeModule) {
107-
this.nativeModule.unload();
108-
}
109-
this.isReadyCallback(false);
110-
this.isGeneratingCallback(false);
111-
}
11228
}
Lines changed: 21 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
import { symbols } from '../constants/ocr/symbols';
2-
import { RnExecutorchErrorCode } from '../errors/ErrorCodes';
3-
import { RnExecutorchError, parseUnknownError } from '../errors/errorUtils';
42
import { ResourceSource } from '../types/common';
53
import { OCRLanguage } from '../types/ocr';
6-
import { ResourceFetcher } from '../utils/ResourceFetcher';
4+
import { BaseOCRController } from './BaseOCRController';
75

8-
export class VerticalOCRController {
9-
private ocrNativeModule: any;
10-
public isReady: boolean = false;
11-
public isGenerating: boolean = false;
12-
public error: string | null = null;
13-
private isReadyCallback: (isReady: boolean) => void;
14-
private isGeneratingCallback: (isGenerating: boolean) => void;
15-
private errorCallback: (error: RnExecutorchError) => void;
16-
17-
constructor({
18-
isReadyCallback = (_isReady: boolean) => {},
19-
isGeneratingCallback = (_isGenerating: boolean) => {},
20-
errorCallback = (_error: RnExecutorchError) => {},
21-
} = {}) {
22-
this.isReadyCallback = isReadyCallback;
23-
this.isGeneratingCallback = isGeneratingCallback;
24-
this.errorCallback = errorCallback;
6+
export class VerticalOCRController extends BaseOCRController {
7+
protected loadNativeModule(
8+
detectorPath: string,
9+
recognizerPath: string,
10+
language: OCRLanguage,
11+
independentCharacters?: boolean
12+
): any {
13+
return global.loadVerticalOCR(
14+
detectorPath,
15+
recognizerPath,
16+
symbols[language],
17+
independentCharacters
18+
);
2519
}
2620

2721
public load = async (
@@ -31,85 +25,12 @@ export class VerticalOCRController {
3125
independentCharacters: boolean,
3226
onDownloadProgressCallback: (downloadProgress: number) => void
3327
) => {
34-
try {
35-
if (!detectorSource || !recognizerSource) return;
36-
37-
if (!symbols[language]) {
38-
throw new RnExecutorchError(
39-
RnExecutorchErrorCode.LanguageNotSupported,
40-
'The provided language for OCR is not supported. Please try using other language.'
41-
);
42-
}
43-
44-
this.isReady = false;
45-
this.isReadyCallback(this.isReady);
46-
47-
const paths = await ResourceFetcher.fetch(
48-
onDownloadProgressCallback,
49-
detectorSource,
50-
recognizerSource
51-
);
52-
if (paths === null || paths.length < 3) {
53-
throw new RnExecutorchError(
54-
RnExecutorchErrorCode.DownloadInterrupted,
55-
'The download has been interrupted. As a result, not every file was downloaded. Please retry the download.'
56-
);
57-
}
58-
this.ocrNativeModule = global.loadVerticalOCR(
59-
paths[0]!,
60-
paths[1]!,
61-
symbols[language],
62-
independentCharacters
63-
);
64-
65-
this.isReady = true;
66-
this.isReadyCallback(this.isReady);
67-
} catch (e) {
68-
if (this.errorCallback) {
69-
this.errorCallback(parseUnknownError(e));
70-
} else {
71-
throw parseUnknownError(e);
72-
}
73-
}
28+
await this.internalLoad(
29+
detectorSource,
30+
recognizerSource,
31+
language,
32+
onDownloadProgressCallback,
33+
independentCharacters
34+
);
7435
};
75-
76-
public forward = async (imageSource: string) => {
77-
if (!this.isReady) {
78-
throw new RnExecutorchError(
79-
RnExecutorchErrorCode.ModuleNotLoaded,
80-
'The model is currently not loaded. Please load the model before calling forward().'
81-
);
82-
}
83-
if (this.isGenerating) {
84-
throw new RnExecutorchError(
85-
RnExecutorchErrorCode.ModelGenerating,
86-
'The model is currently generating. Please wait until previous model run is complete.'
87-
);
88-
}
89-
90-
try {
91-
this.isGenerating = true;
92-
this.isGeneratingCallback(this.isGenerating);
93-
return await this.ocrNativeModule.generate(imageSource);
94-
} catch (e) {
95-
throw parseUnknownError(e);
96-
} finally {
97-
this.isGenerating = false;
98-
this.isGeneratingCallback(this.isGenerating);
99-
}
100-
};
101-
102-
public delete() {
103-
if (this.isGenerating) {
104-
throw new RnExecutorchError(
105-
RnExecutorchErrorCode.ModelGenerating,
106-
'The model is currently generating. Please wait until previous model run is complete.'
107-
);
108-
}
109-
if (this.ocrNativeModule) {
110-
this.ocrNativeModule.unload();
111-
}
112-
this.isReadyCallback(false);
113-
this.isGeneratingCallback(false);
114-
}
11536
}

0 commit comments

Comments
 (0)