Skip to content

Commit 920d813

Browse files
committed
final
1 parent d66ecb3 commit 920d813

14 files changed

+232
-138
lines changed

Backend/accounts/naverAPI.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ def image_NAVER_AI(img_64):
8484

8585
maerong = []
8686

87-
client_id = "AUXR0gR5NNTjtmnyWdDD" # 개발자센터에서 발급받은 Client ID 값
88-
client_secret = "u6hHsOvN5g" # 개발자센터에서 발급받은 Client Secret 값
87+
client_id = "NlmxERtXxumnUqV8ZeWn" # 개발자센터에서 발급받은 Client ID 값
88+
client_secret = "fHT22NImgb" # 개발자센터에서 발급받은 Client Secret 값
8989
for key, val in receipt.items():
9090
if key == '':
9191
continue

Backend/accounts/views.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from django.views.decorators.csrf import csrf_exempt
99
from .models import *
1010
from .serializers import *
11-
# from naver.classify import is_receipt
11+
from naver.classify import is_receipt
1212
from .naverAPI import image_NAVER_AI
1313
import json
1414

Backend/naver/classify.py

+69-69
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,81 @@
1-
# import torch
2-
# import torch.nn as nn
3-
# import torch.nn.init
4-
# import torchvision
5-
# from torchvision import models
6-
# import torchvision.transforms as transforms
7-
# import torch.nn.init
8-
# from PIL import Image
1+
import torch
2+
import torch.nn as nn
3+
import torch.nn.init
4+
import torchvision
5+
from torchvision import models
6+
import torchvision.transforms as transforms
7+
import torch.nn.init
8+
from PIL import Image
99

10-
# class CNN(nn.Module):
11-
# def __init__(self):
12-
# super(CNN, self).__init__()
13-
# self.layer1 = nn.Sequential(
14-
# nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1),
15-
# nn.BatchNorm2d(32),
16-
# nn.ReLU(),
17-
# nn.MaxPool2d(2)
18-
# )
19-
# self.layer2 = nn.Sequential(
20-
# nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
21-
# nn.BatchNorm2d(64),
22-
# nn.ReLU(),
23-
# nn.MaxPool2d(2)
24-
# )
25-
# self.layer3 = nn.Sequential(
26-
# nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
27-
# nn.BatchNorm2d(128),
28-
# nn.ReLU(),
29-
# nn.MaxPool2d(2)
30-
# )
10+
class CNN(nn.Module):
11+
def __init__(self):
12+
super(CNN, self).__init__()
13+
self.layer1 = nn.Sequential(
14+
nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1),
15+
nn.BatchNorm2d(32),
16+
nn.ReLU(),
17+
nn.MaxPool2d(2)
18+
)
19+
self.layer2 = nn.Sequential(
20+
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
21+
nn.BatchNorm2d(64),
22+
nn.ReLU(),
23+
nn.MaxPool2d(2)
24+
)
25+
self.layer3 = nn.Sequential(
26+
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
27+
nn.BatchNorm2d(128),
28+
nn.ReLU(),
29+
nn.MaxPool2d(2)
30+
)
3131

32-
# self.fc1 = nn.Linear(32 * 32 * 128, 625)
33-
# self.relu = nn.ReLU()
34-
# self.fc2 = nn.Linear(625, 2, bias=True)
35-
# torch.nn.init.xavier_uniform_(self.fc1.weight)
36-
# torch.nn.init.xavier_uniform_(self.fc2.weight)
32+
self.fc1 = nn.Linear(32 * 32 * 128, 625)
33+
self.relu = nn.ReLU()
34+
self.fc2 = nn.Linear(625, 2, bias=True)
35+
torch.nn.init.xavier_uniform_(self.fc1.weight)
36+
torch.nn.init.xavier_uniform_(self.fc2.weight)
3737

38-
# def forward(self, x):
39-
# out = self.layer1(x)
40-
# out = self.layer2(out)
41-
# # print(out.shape)
42-
# out = self.layer3(out)
43-
# # print(out.shape)
44-
# out = out.view(out.size(0), -1)
45-
# out = self.fc1(out)
46-
# out = self.relu(out)
47-
# out = self.fc2(out)
48-
# return out
38+
def forward(self, x):
39+
out = self.layer1(x)
40+
out = self.layer2(out)
41+
# print(out.shape)
42+
out = self.layer3(out)
43+
# print(out.shape)
44+
out = out.view(out.size(0), -1)
45+
out = self.fc1(out)
46+
out = self.relu(out)
47+
out = self.fc2(out)
48+
return out
4949

5050

51-
# device = 'cuda' if torch.cuda.is_available() else 'cpu'
52-
# # device = 'cpu'
51+
device = 'cuda' if torch.cuda.is_available() else 'cpu'
52+
# device = 'cpu'
5353

54-
# torch.manual_seed(777)
55-
# if device == 'cuda':
56-
# torch.cuda.manual_seed_all(777)
54+
torch.manual_seed(777)
55+
if device == 'cuda':
56+
torch.cuda.manual_seed_all(777)
5757

58-
# def is_receipt(img_file):
59-
# model_v2 = CNN().to(device)
60-
# model_v2.load_state_dict(torch.load('./static/model_v2.pth'))
58+
def is_receipt(img_file):
59+
model_v2 = CNN().to(device)
60+
model_v2.load_state_dict(torch.load('./static/model_v2.pth'))
6161

62-
# img = Image.open(img_file)
62+
img = Image.open(img_file)
6363

64-
# # img = Image.open("./static/imgs/3.png")
65-
# # png to jpg
66-
# img = img.convert('RGB')
64+
# img = Image.open("./static/imgs/3.png")
65+
# png to jpg
66+
img = img.convert('RGB')
6767

68-
# resize = transforms.Resize([256, 256])
69-
# img = resize(img)
70-
# totensor = transforms.ToTensor()
71-
# img = totensor(img).unsqueeze(0).to(device)
72-
# img.shape
73-
# # test
74-
# with torch.no_grad():
75-
# prediction = model_v2(img)
76-
# prediction_value = torch.argmax(prediction, 1)
77-
# value = prediction_value.data.cpu().numpy()[0]
68+
resize = transforms.Resize([256, 256])
69+
img = resize(img)
70+
totensor = transforms.ToTensor()
71+
img = totensor(img).unsqueeze(0).to(device)
72+
img.shape
73+
# test
74+
with torch.no_grad():
75+
prediction = model_v2(img)
76+
prediction_value = torch.argmax(prediction, 1)
77+
value = prediction_value.data.cpu().numpy()[0]
7878

79-
# return value
79+
return value
8080

81-
# # print(is_receipt())
81+
# print(is_receipt())

Backend/naver/cnn_model.py

-5
This file was deleted.
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
## (OCR API, Papago API )+ 환율 API
2+
3+
### 1. Naver OCR 호출
4+
5+
OCR API를 적용할 때에 가장 중요하게 고려한 것은 인식률과 Papago API 연동 시, 번역의 완성도였다.
6+
7+
이를 고려하여 다양한 방법으로 진행해봤다. 우선은 기본으로 제공하는 General Text 추출을 시도해봤고, 꽤 뛰어난 인식률에 비해, 결과 값의 줄 구분이 안 되어있어, Papago API에서 번역 값이 잘 나오지 않았다.
8+
9+
![](./assets/General_OUTBACK.PNG)
10+
11+
12+
13+
또 다른 방법으로 영수증을 삼등분하여 header(보통 상호명과 주소, 전화번호 등 Information이 있는 구역), body(소비내역이 주로 적혀있다), footer(총 합계 및 다른 부가적인 계산 요소들이 적혀있다.)로 필드를 나누어 인식했으나, 이 또한 번역에서 원하는 결과 값을 얻지 못했다.
14+
15+
![](./assets/3등분.PNG)
16+
17+
정말 원하는 결과를 받고 싶으면, 추출하고 싶은 값이 있는 부분에만 구체적인 필드를 지정하여 OCR 요청하는 방법도 있었으나, 이를 할 경우, 등록되지 않은 템플릿을 돌릴 경우 전혀 다른 결과가 나올 수 있기 때문에 시도하다가 포기했다.
18+
19+
![](./assets/Specific.PNG)
20+
21+
22+
23+
이러한 시행착오 끝에 정한 방법은 한 줄 단위로 필드를 지정하여 OCR에 요청하고 약간의 후 처리과정을 거쳐 나온 결과 값을 Papago API에 요청하는 것이었다. 이러한 방법을 쓴 이유는 대체적으로 한 줄 안에 소비제품 명과 가격이 다 나와있고, 한 줄을 단위로 내용이 종결되는 경우가 많기 때문이었다. 한 멘토님의 조언에 따르면, 이 방법은 실제로도 현업에서 쓰이고 있다고 하며 인식률과 번역 완성도를 고려했을 때, 가장 나은 방법이라고 평가하였다.
24+
25+
![](./assets/한 줄.PNG)
26+
27+
### 2. 결과 값 후 처리
28+
29+
한 줄을 단위로 인식된 텍스트 값이 나왔다. 모든 텍스트를 있는 그대로 저장할 수도 있지만, 우리 팀이 만드는 서비스 상, 상호명, 소비 내역, 총 합계가 중요하기에 다른 부가적인 요소들을 어떻게 효과적으로 걸러낼 지에 대해 고민했다.
30+
31+
![](./assets/코드 구성.PNG)
32+
33+
여러 방법에 대해 고민했고 그 결과 완벽하지는 않지만, 80% 정도의 완성률을 보이는 알고리즘을 짤 수 있었다. (필드의 마지막 값은 실수여야 되고, 가격에는 '.' 이나 '$'가 들어가야되며, 총합을 나타내는 말이 안들어가면 일반소비로 분류하는 등 다양한 기준을 넣어 결과 값을 만들 어 줬다.) 이를 바탕으로 결국 품목(key)과 가격(value)로 구성 된 receipt라는 딕셔너리를 만들 수 있었다.
34+
35+
36+
37+
### 3. Papago에 번역 요청 및 후 처리
38+
39+
영어로 나타낸 receipt 딕셔너리의 key, value 값을 파파고에 요청하여 번역했다. 이를 각각 recepit_trans라는 한국어로 번역 된 딕셔너리에 저장했는데, 후 처리가 정말 중요했다. 예를 들어 영수증에 '거스름 돈'을 나타낼 때 영어 'Change'단어를 쓰는 경우가 많지만, 이를 파파고에 돌릴 경우 '바꾸다'라는 결과 값이 나온다. 이와 같이, 파파고에서 적절치 못한 번역을 하는 경우, 후 처리를 통해 적절한 말로 바꿔주었다. 결과는 꽤나 성공적인 결과 값이 나오는 경우가 많았다.
40+
41+
![](./assets/파파고 번역 결과 값.PNG)
42+
43+
44+
45+
### 4. 추가로 환율API 불러오기
46+
47+
우리가 만드는 서비스는 한 영수증에 대한 소비 합계를 불러와야 하는 것은 물론, 해당 금액에 대한 환율을 적용하여 개인 소비 내역에 저장하고, 누적금액까지 구해 주는 기능을 갖고 있다. 이를 위해 수출입은행 환율 API를 이용했고, 이전에 Papago에서 'Total' 이나 '총'이라는 key 값을 가진 경우 해당 value에 환율을 곱하여 개인 소비 내역에 전송했다.

ETC/assets/3등분.PNG

752 KB
Loading

ETC/assets/General.PNG

158 KB
Loading

ETC/assets/General_OUTBACK.PNG

607 KB
Loading

ETC/assets/Specific.PNG

775 KB
Loading

ETC/assets/코드 구성.PNG

32.8 KB
Loading
40.5 KB
Loading

ETC/assets/한 줄.PNG

776 KB
Loading

Frontend/package-lock.json

+52
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)