File size: 11,515 Bytes
6ef9ea4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# https://planogram-compliance.herokuapp.com/
# https://dashboard.heroku.com/apps/planogram-compliance/deploy/heroku-git

# https://medium.com/@mohcufe/how-to-deploy-your-trained-pytorch-model-on-heroku-ff4b73085ddd\
# https://stackoverflow.com/questions/51730880/where-do-i-get-a-cpu-only-version-of-pytorch
# https://blog.jcharistech.com/2020/02/26/how-to-deploy-a-face-detection-streamlit-app-on-heroku/
# https://towardsdatascience.com/a-quick-tutorial-on-how-to-deploy-your-streamlit-app-to-heroku-
# https://www.analyticsvidhya.com/blog/2021/06/deploy-your-ml-dl-streamlit-application-on-heroku/
# https://gist.github.com/jeremyjordan/6b506257509e8ba673f145baa568a1ea

import json

# https://www.r-bloggers.com/2020/12/creating-a-streamlit-web-app-building-with-docker-github-actions-and-hosting-on-heroku/
# https://devcenter.heroku.com/articles/container-registry-and-runtime
# from yolo_inference_util import run_yolo_v5
import os
from tempfile import NamedTemporaryFile

import cv2
import numpy as np
import pandas as pd
import streamlit as st

# import matplotlib.pyplot as plt
from app_utils import annotate_planogram_compliance, bucket_sort, do_sorting, xml_to_csv
from inference import run

# from utils.plots import Annotator, colors
# from utils.general import scale_coords

app_formal_name = "Planogram Compliance"

FILE_UPLOAD_DIR = "tmp"

os.makedirs(FILE_UPLOAD_DIR, exist_ok=True)
# Start the app in wide-mode
st.set_page_config(
    layout="wide",
    page_title=app_formal_name,
)
# https://github.com/streamlit/streamlit/issues/1361
uploaded_file = st.file_uploader(
    "Choose a planogram image to score",
    type=["jpg", "JPEG", "PNG", "JPG", "jpeg"],
)
uploaded_master_planogram_file = st.file_uploader(
    "Upload a master planogram", type=["jpg", "JPEG", "PNG", "JPG", "jpeg"]
)
annotation_file = st.file_uploader("upload master polanogram", type=["xml"])
temp_file = NamedTemporaryFile(delete=False)

target_names = [
    "Bottle,100PLUS ACTIVE 1.5L",
    "Bottle,100PLUS ACTIVE 500ML",
    "Bottle,100PLUS LEMON LIME 1.5L",
    "Bottle,100PLUS ORANGE 500ML",
    "Bottle,100PLUS ORIGINAL 1.5L",
    "Bottle,100PLUS TANGY ORANGE 1.5L",
    "Bottle,100PLUS ZERO 1.5L",
    "Bottle,100PLUS ZERO 500ML",
    "Packet,F:M MAGNOLIA CHOC 1L",
    "Bottle,F&N GINGER ADE 1.5L",
    "Bottle,F&N GRAPE 1.5L",
    "Bottle,F&N ICE CREAM SODA 1.5L",
    "Bottle,F&N LYCHEE PEAR 1.5L",
    "Bottle,F&N ORANGE 1.5L",
    "Bottle,F&N PINEAPPLE PET 1.5L",
    "Bottle,F&N SARSI 1.5L",
    "Bottle,F&N SS ICE LEM TEA RS 500ML",
    "Bottle,F&N SS ICE LEMON TEA RS 1.5L",
    "Bottle,F&N SS ICE LEMON TEA 1.5L",
    "Bottle,F&N SS ICE LEMON TEA 500ML",
    "Bottle,F&N SS ICE PEACH TEA 1.5L",
    "Bottle,SS ICE LEMON GT 1.48L",
    "Bottle,SS WHITE CHRYS TEA 1.48L",
    "Packet,FARMHOUSE FRESH MILK 1L FNDM",
    "Packet,FARMHOUSE PLAIN LF 1L",
    "Packet,PURA FRESH MILK 1L FS",
    "Packet,NUTRISOY REG NO SUGAR ADDED 1L",
    "Packet,NUTRISOY PLAIN 475ML",
    "Packet,NUTRISOY PLAIN 1L",
    "Packet,NUTRISOY OMEGA RD SUGAR 1L",
    "Packet,NUTRISOY OMEGA NSA 1L",
    "Packet,NUTRISOY ALMOND 1L",
    "Packet,MAGNOLIA FRESH MILK 1L FNDM",
    "Packet,FM MAG FC PLAIN 200ML",
    "Packet,MAG OMEGA PLUS PLAIN 200ML",
    "Packet,MAG KURMA MILK 500ML",
    "Packet,MAG KURMA MILK 1L",
    "Packet,MAG CHOCOLATE FC 500ML",
    "Packet,MAG BROWN SUGAR SS MILK 1L",
    "Packet,FM MAG LFHC PLN 500ML",
    "Packet,FM MAG LFHC OAT 500ML",
    "Packet,FM MAG LFHC OAT 1L",
    "Packet,FM MAG FC PLAIN 500ML",
    "Void,PARTIAL VOID",
    "Void,FULL VOID",
    "Bottle,F&N SS ICE LEM TEA 500ML",
]

run_app = st.button("Run the compliance check")
if run_app and uploaded_file is not None:
    # Convert the file to an opencv image.
    file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
    temp_file.write(uploaded_file.getvalue())
    uploaded_img = cv2.imdecode(file_bytes, 1)
    cv2.imwrite("tmp/to_score_planogram_tmp.png", uploaded_img)

    # if uploaded_master_planogram_file is None:
    #     master = cv2.imread('./sample_master_planogram.jpeg')

    names_dict = {name: id for id, name in enumerate(target_names)}

    sorted_xml_df = None
    # https://discuss.streamlit.io/t/unable-to-read-files-using-standard-file-uploader/2258/2
    if uploaded_master_planogram_file and annotation_file:
        file_bytes = np.asarray(
            bytearray(uploaded_master_planogram_file.read()), dtype=np.uint8
        )
        master = cv2.imdecode(file_bytes, 1)
        cv2.imwrite("tmp/master_tmp.png", master)
        # cv2.imwrite("tmp_uploaded_master_planogram_img.png", master)
        # xml = annotation_file.read()
        # tmp_xml ="tmp_xml_annotation.xml"
        # with open(tmp_xml ,'w',encoding='utf-8') as f:
        #      xml = f.write(xml)
        xml_df = xml_to_csv(annotation_file)
        xml_df["cls"] = xml_df["cls"].map(names_dict)
        sorted_xml_df = do_sorting(xml_df)
        sorted_xml_df.line_number.value_counts()

        line_data = sorted_xml_df.line_number.value_counts()
        n_rows = int(len(line_data))
        n_cols = int(max(line_data))
        master_table = np.zeros((n_rows, n_cols)) + 101
        master_annotations = []
        for i, row in sorted_xml_df.groupby("line_number"):
            # print(f"Adding products in the row {i} to the detected planogram", row.cls.tolist())
            products = row.cls.tolist()
            master_table[int(i - 1), 0 : len(products)] = products
            annotations = [
                (int(k), int(v))
                for k, v in list(
                    zip(row.cls.unique(), row.cls.value_counts().tolist())
                )
            ]
            master_annotations.append(annotations)
        master_table.shape
        # print("Annoatated planogram")
        # print(np.matrix(master_table))

    elif uploaded_master_planogram_file:
        print(
            "Finding the amster annotations with the YOLOv5 model predictions"
        )
        file_bytes = np.asarray(
            bytearray(uploaded_master_planogram_file.read()), dtype=np.uint8
        )
        master = cv2.imdecode(file_bytes, 1)
        cv2.imwrite("tmp/master_tmp.png", master)
        master_results = run(
            weights="base_line_best_model_exp5.pt",
            source="tmp/master_tmp.png",
            imgsz=[640, 640],
            conf_thres=0.6,
            iou_thres=0.6,
        )

        bb_df = pd.DataFrame(
            master_results[0][1].tolist(),
            columns=["xmin", "ymin", "xmax", "ymax", "conf", "cls"],
        )
        sorted_df = do_sorting(bb_df)

        n_rows = int(sorted_df.line_number.max())
        n_cols = int(
            sorted_df.groupby("line_number")
            .size()
            .reset_index(name="counts")["counts"]
            .max()
        )
        non_null_product = 101
        print("master size", n_rows, n_cols)
        master_annotations = []
        master_table = np.zeros((int(n_rows), int(n_cols))) + non_null_product
        for i, row in sorted_df.groupby("line_number"):
            # print(f"Adding products in the row {i} to the detected planogram", row.cls.tolist())
            products = row.cls.tolist()
            col_len = min(len(products), n_cols)
            print("col size: ", col_len)
            print("row size: ", i - 1)
            if n_rows <= (i - 1):
                print("more rows than expected in the predictions")
                break
            master_table[int(i - 1), 0:col_len] = products[:col_len]
            annotations = [
                (int(k), int(v))
                for k, v in list(
                    zip(row.cls.unique(), row.cls.value_counts().tolist())
                )
            ]
            master_annotations.append(annotations)
    else:
        master = cv2.imread("./sample_master_planogram.jpeg")
        n_rows = 3
        n_cols = 16
        master_table = np.zeros((n_rows, n_cols)) + 101
        master_annotations = [
            [(32, 12), (8, 4)],
            [(36, 1), (41, 6), (50, 4), (51, 3), (52, 2)],
            [(23, 5), (24, 6), (54, 5)],
        ]

        for i, row in enumerate(master_annotations):
            idx = 0
            for product, count in row:
                master_table[i, idx : idx + count] = product
                idx = idx + count
    # Now do something with the image! For example, let's display it:
    # st.image(opencv_image, channels="BGR")

    # uploaded_img = '/content/drive/My Drive/0.CV/0.Planogram_Compliance/planogram_data/images/test/IMG_5718.jpg'
    result_list = run(
        weights="base_line_best_model_exp5.pt",
        source="tmp/to_score_planogram_tmp.png",
        imgsz=[640, 640],
        conf_thres=0.6,
        iou_thres=0.6,
    )

    bb_df = pd.DataFrame(
        result_list[0][1].tolist(),
        columns=["xmin", "ymin", "xmax", "ymax", "conf", "cls"],
    )
    sorted_df = do_sorting(bb_df)

    non_null_product = 101
    print("master size", n_rows, n_cols)
    detected_table = np.zeros((n_rows, n_cols)) + non_null_product
    for i, row in sorted_df.groupby("line_number"):
        # print(f"Adding products in the row {i} to the detected planogram", row.cls.tolist())
        products = row.cls.tolist()
        col_len = min(len(products), n_cols)
        print("col size: ", col_len)
        print("row size: ", i - 1)
        if n_rows <= (i - 1):
            print("more rows than expected in the predictions")
            break
        detected_table[int(i - 1), 0:col_len] = products[:col_len]

    # score = (master_table == detected_table).sum() / (master_table != non_null_product).sum()
    correct_matches = (
        np.ma.masked_equal(master_table, non_null_product) == detected_table
    ).sum()
    total_products = (master_table != non_null_product).sum()
    score = correct_matches / total_products
    # if sorted_xml_df is not None:
    #     annotate_df = sorted_xml_df[["xmin","ymin", "xmax", "ymax", "line_number","cls"]].astype(int)
    # else:
    annotate_df = sorted_df[
        ["xmin", "ymin", "xmax", "ymax", "line_number", "cls"]
    ].astype(int)

    mask = master_table != non_null_product
    m_detected_table = np.ma.masked_array(master_table, mask=mask)
    m_annotated_table = np.ma.masked_array(detected_table, mask=mask)

    #  wrong_indexes = np.ravel_multi_index(master_table*mask != detected_table*mask, master_table.shape)
    wrong_indexes = np.where(master_table != detected_table)
    correct_indexes = np.where(master_table == detected_table)
    annotated_planogram = annotate_planogram_compliance(
        uploaded_img, annotate_df, correct_indexes, wrong_indexes, target_names
    )
    st.title("Target Products")
    st.write(json.dumps(target_names))
    st.title("The master planogram annotation")
    st.write(
        "The annotations are based on the index of products from Target products list "
    )
    st.write(json.dumps(master_annotations))

    # https://github.com/streamlit/streamlit/issues/888
    st.image(
        [master, annotated_planogram, result_list[0][0]],
        width=512,
        caption=[
            "Master planogram",
            "Planogram Compliance",
            "Planogram Predictions",
        ],
        channels="BGR",
    )
    # st.image([master, annotated_planogram], width=512, caption=["Master planogram",  "Planogram Compliance"],  channels="BGR")
    st.title("Planogram Compiance score")
    # st.write(f"{correct_matches} / {total_products}")
    st.write(score)