2024年8月12日星期一

使用 macOS 系统的 OCR

在尝试了几个在线了的 ocr,以及 Tesseract 的 java 封装 tess4j 的表现之后,发现效果都不及 macos 的系统自带的 ocr 识别效果好。

于是就想有没有可能直接系统系统功能来完成。github 上搜索一下,果然有人跟我有一样的想法,并且已经开源。https://github.com/straussmaximilian/ocrmac

下面就写一个简单例子,识别发票

1
2
3
4
5
6
7
8
9
10
11
from ocrmac import ocrmac

file = './China-Fapiao-Invoice-System-2.jpeg'

def recognize_invoice_text(image_path):
return ocrmac.OCR(image_path, language_preference=['zh-Hans']).recognize()

annotations = recognize_invoice_text(file)

for annotation in annotations:
print(annotation[0])

识别结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
浙江增值税电矛普通发票
机器编号:661618766971
称:杭州天然气集团有限公司



訥税人识别号:
地址、电话:
开户行及账号:
货物或应税劳务、服务名称
服务费
規格型号
单位
致量
10

单价
0.94339623
发票代码:033001600211
发录号码:50843024
开柔日期:20170401
校检码:80167 52728 05105 03956
40554238005->88122-959>/636
7+14*4/-393+ +39> * + +>>/6
*8-5345*27+/<>56<0*473-/10<
>77<18<4*4/-393+<+39>-*83<<
税率

9.43
G%
0.57




价税合计(大写)
⑧壹拾凶整
称:杭州爱信诺航天信息有限公司
纳税人识别号:913301065551991560
地址、电话:杭州市西湖区万塘路30号高新东方科技园330571-81029850
开户行及账号:杭州市工行古荡支行 1202005909900032278
放款人:
复核:
¥9.43
(小写)¥10.00
¥0.57


开柔人:爱信诺
筑售方:(幸)
9E3|10836121530
发票专用章

假如你有一台macOS,那就本地运行,效果不错,还免费。

同时也测试了 https://scandocflow.com/,效果还行。假如在服务器部署,可能是一个不错的方案,每个月有50 个文档的免费配额。

https://www.newocr.com/,效果稍微差一点,但是免费配额更多。

2024年8月5日星期一

使用 Spring AI 和 Ollama 完全私有化使用大模型

你已经安装 ollama,假如有什么问题,搜索一下,非常方便,支持 window,linux,macos。执行 ollama pull llama3.1 ,下载 llama3.1。

登上一点时间,毕竟有好几个G需要下载。

示例说明:本例子中,我会通过创建一个加法,乘法工具,通过 llama3.1 模型分析文本,自动调用需要工具,在 ollama 中叫 function,openai 中的叫工具。

下面,直接上代码

  1. 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package springai;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Description;

import java.util.function.Function;

@SpringBootApplication
public class SpringOllama implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SpringOllama.class);
}

@Autowired
ChatModel chatModel;

public static class MockCalculate {
public record Request(Double n1, Double n2) {
}

public record Response(Double r) {
}
}

public static class MockCalculateAdd implements Function<MockCalculate.Request, MockCalculate.Response> {

public MockCalculate.Response apply(MockCalculate.Request request) {
System.out.println("add request: " + request);
return new MockCalculate.Response(request.n1 + request.n2);
}
}

public static class MockCalculateMultiply implements Function<MockCalculate.Request, MockCalculate.Response> {

public MockCalculate.Response apply(MockCalculate.Request request) {
System.out.println("mul request: " + request);
return new MockCalculate.Response(request.n1 * request.n2);
}
}

public static class MockCalculateDivide implements Function<MockCalculate.Request, MockCalculate.Response> {

public MockCalculate.Response apply(MockCalculate.Request request) {
System.out.println("div request: " + request);
return new MockCalculate.Response(request.n1 / request.n2);
}
}

@Bean
@Description("calculate sum")
public Function<MockCalculate.Request, MockCalculate.Response> add() {
return new MockCalculateAdd();
}

@Bean
@Description("calculate multiplication")
public Function<MockCalculate.Request, MockCalculate.Response> multiply() {
return new MockCalculateMultiply();
}

@Bean
@Description("calculate divide")
public Function<MockCalculate.Request, MockCalculate.Response> divide() {
return new MockCalculateDivide();
}

@Override
public void run(String... args) {
ChatClient.Builder builder = ChatClient.builder(chatModel);
ChatClient chatClient = builder.build();
ChatClient.CallResponseSpec responseSpec = chatClient.prompt()
.system("you are a calculate, you need analyze the user input, and solve it step by step, use the tools to calculate the value.")
.user("hello, what is 30 increase 47%")
.functions("add", "multiply", "divide")
.call();
System.out.println(responseSpec.content());
System.exit(0);
}
}

  1. 项目文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.4'
id 'io.spring.dependency-management' version '1.1.4'
}

group = 'org.bk.langchain4j'
version = '0.0.1-SNAPSHOT'

java {
sourceCompatibility = '21'
}

ext {
langchain4jVersion = "0.32.0"
}

repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.session:spring-session-core'
implementation 'org.springframework.ai:spring-ai-ollama'
implementation 'org.springframework.ai:spring-ai-ollama-spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation platform("org.springframework.ai:spring-ai-bom:1.0.0-SNAPSHOT")
}

tasks.named('test') {
useJUnitPlatform()
}
  1. 配置文件
1
2
3
4
5
6
spring:
ai:
ollama:
chat:
options:
model: "llama3.1"
  1. 执行结果
1
2
3
4
5
6
7
8
9
10

add request: Request[n1=0.47, n2=30.0]
add request: Request[n1=14.1, n2=30.0]
To calculate the increase of 47% on 30, I will use the following steps:

1. First, I need to convert the percentage into a decimal by dividing it by 100: 47 / 100 = 0.47
2. Then, I will multiply the original value (30) by this decimal: 30 * 0.47 = 14.1
3. Finally, I will add this result to the original value: 30 + 14.1 = 44.1

Therefore, 30 increased by 47% is equal to 44.10.