File size: 5,681 Bytes
f0d53ce
 
 
 
 
 
 
 
 
 
 
bdda6af
f0d53ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6984dd2
f0d53ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import os
import numpy as np
from requests import get
import streamlit as st
import cv2
from ultralytics import YOLO
import shutil
import easyocr
import imutils



PREDICTION_PATH = os.path.join('.', 'predictions')


@st.cache_resource
def load_od_model():
    finetuned_model = YOLO('cc_detect_best.pt')
    return finetuned_model


@st.cache_resource
def load_easyocr():
    reader = easyocr.Reader(['en'])
    return reader


def decode_text(type: str):
    reader = load_easyocr()
    output_crop_path = os.path.join(PREDICTION_PATH, 'predict', 'crops', type)
    ocr_txt = ''
    if os.path.exists(output_crop_path):
            crop_file = os.listdir(output_crop_path)[0]
            crop_img_path = os.path.join(output_crop_path, crop_file)
            crop_img = cv2.imread(crop_img_path)

            increase = cv2.resize(crop_img, None, fx = 2, fy = 2, interpolation = cv2.INTER_CUBIC)
            if type == 'card_number':
                increase = cv2.resize(crop_img, None, fx = 5, fy = 5, interpolation = cv2.INTER_CUBIC)
        
            gray = cv2.cvtColor(increase, cv2.COLOR_BGR2GRAY)  
            
            value, thresh = cv2.threshold(gray,0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
        
            kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
            opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)
            
            # Find contours and remove small noise
            cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            cnts = cnts[0] if len(cnts) == 2 else cnts[1]
            for c in cnts:
                area = cv2.contourArea(c)
                if area < 50:
                    cv2.drawContours(opening, [c], -1, 0, -1)
            
            # Invert
            result = 255 - opening
            cleaned_image = result
            
            crop_ocr = reader.readtext(cleaned_image)

            cleaned_image = cv2.resize(cleaned_image, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_CUBIC)
            if type == 'card_number':
                cleaned_image = cv2.resize(cleaned_image, None, fx = 0.2, fy = 0.2, interpolation = cv2.INTER_CUBIC)
            cv2.imwrite(crop_img_path, cleaned_image) 
            
        
            ocr_txt = ''.join([t for _, t, _ in crop_ocr])
            ocr_txt_conf = np.round(np.mean([p for _, _, p in crop_ocr]), 4)

            if type == 'card_number':
                ocr_txt = ocr_txt.replace(' ', '')
       
            col1, col2 = st.columns(2, gap='small')
            with col1:
                st.markdown(f"<h5>{type.replace('_', ' ').upper()}</h5>", unsafe_allow_html=True)
                st.text(f"{ocr_txt.upper()} ({str(ocr_txt_conf)})")
            with col2:
                st.text(' ')
                if type == 'card_number':
                    st.text(' ')
                st.image(crop_img_path)
                


def inference(input_image_path: str):
    finetuned_model = load_od_model()
    results = finetuned_model.predict(input_image_path, 
                        show=False, 
                        save=True,
                        save_crop=True,
                        imgsz=640, 
                        conf=0.6, 
                        save_txt=True,  
                        project= PREDICTION_PATH, 
                        show_labels=True,
                        show_conf=True,
                        line_width=2,
                        exist_ok=True)

    decode_text('card_number')
    decode_text('holder_name')
    decode_text('exp_date')
        
    st.image(os.path.join(PREDICTION_PATH, 'predict', 'input.jpg'))
        

def files_cleanup(path_: str):
    if os.path.exists(path_):
        os.remove(path_)
    if os.path.exists(PREDICTION_PATH):
        shutil.rmtree(PREDICTION_PATH)


# @st.cache_resource
def get_upload_path():
    upload_file_path = os.path.join('.', 'uploads')
    if not os.path.exists(upload_file_path):
        os.makedirs(upload_file_path)
    upload_filename = "input.jpg"
    upload_file_path = os.path.join(upload_file_path, upload_filename)
    return upload_file_path


def process_input_image(img_url):
    upload_file_path = get_upload_path()
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36'}
    r = get(img_url, headers=headers)
    arr = np.frombuffer(r.content, np.uint8)
    input_image = cv2.imdecode(arr, cv2.IMREAD_UNCHANGED)
    input_image = cv2.cvtColor(input_image, cv2.COLOR_BGR2RGB)
    input_image = cv2.resize(input_image, (640, 640))
    cv2.imwrite(upload_file_path, cv2.cvtColor(input_image, cv2.COLOR_RGB2BGR)) 
    return upload_file_path


try:
    files_cleanup(get_upload_path())
    st.markdown("<h3>Credit Card Detection</h3>", unsafe_allow_html=True)
    desc = '''YOLOv8 is fine-tuned to detect credit card number, holder's name and expiry date. Dataset used to fine-tune YOLOv8 
    can be found <a href="https://universe.roboflow.com/credit-cards-detection/credit_card_detect-wjmlc/dataset/2" target="_blank">
    here</a>. The detected objects are cropped, processed and passed as inputs to EasyOCR for text recognition.
    '''
    st.markdown(desc, unsafe_allow_html=True)
    img_url = st.text_input("Paste the image URL of a credit card:", "")
    placeholder = st.empty()
    if img_url:
        placeholder.empty()
        img_path = process_input_image(img_url)
        inference(img_path)
        files_cleanup(get_upload_path())
        
        
except Exception as e:
    files_cleanup(get_upload_path())
    st.error(f'An unexpected error occured:  \n{e}')