diff --git a/algorithm/product.py b/algorithm/product.py index 59eda487501b33319e9885332575389cade4e8f6..05f0ac99f193134cf4200fa2136dbb5ff540f4e6 100644 --- a/algorithm/product.py +++ b/algorithm/product.py @@ -1,8 +1,7 @@ class Product: - def __init__(self, name, price, quantity): + def __init__(self, name, price): self.name = name self.price = price - self.quantity = quantity def __repr__(self): - return f"Product(name={self.name}, price={self.price}, quantity={self.quantity})" + return f"Product(name={self.name}, price={self.price})" diff --git a/algorithm/receipt.py b/algorithm/receipt.py deleted file mode 100644 index 600dc2c138bdc2303069bc56cf6f1f84f7c1d457..0000000000000000000000000000000000000000 --- a/algorithm/receipt.py +++ /dev/null @@ -1,8 +0,0 @@ -class Receipt: - def __init__(self, products, tax, total_amount): - self.products = products - self.tax = tax - self.total_amount = total_amount - - def __repr__(self): - return f"Receipt(products={self.products}, tax={self.tax}, total_amount={self.total_amount})" diff --git a/algorithm/receipt_calculation.py b/algorithm/receipt_calculation.py index 093aefc396162510b1ffc764625b8dc239d3d2c9..0ab43d7452876bfbadbae3484bbb4cb03cddfc41 100644 --- a/algorithm/receipt_calculation.py +++ b/algorithm/receipt_calculation.py @@ -93,21 +93,21 @@ def calculate_tips_and_taxes(items_table, total_amount, tax, tips): if items_table[0][0] == "No items": return products, 0 - if total_amount == "Not specified" or total_amount is None: + if total_amount == "Not specified" or total_amount == "unknown" or total_amount is None: total_amount = "0.0" - if tax == "Not specified" or tax is None: + if tax == "Not specified" or tax == "unknown" or tax is None: tax = "0.0" - if tips == "Not specified" or tips is None: - tax = "0.0" + if tips == "Not specified" or tips == "unknown" or tips is None: + tips = "0.0" for item in items_table: price = item[5] - if price == "Not specified": + if price == "Not specified" or price == "unknown": price = "0.0" item_value = float(str(price).replace(",", ".")) if item[5] is not None else 0.0 - products.append(Product(item[0], item_value, item[3])) + products.append(Product(item[0], item_value)) sum_of_product_prices = 0 for _product in products: diff --git a/app.py b/app.py index 9a39ae8ca0c213f491d5d054e140abacb8830c3e..7653ca96c789f4fa9625267f9d155b92a0a19fcb 100644 --- a/app.py +++ b/app.py @@ -14,6 +14,10 @@ from vertex_ai_service import VertexAIService model_names = ["gemini-1.5-flash", "gemini-1.5-pro", "gpt-4o-mini", "gpt-4o", "gpt-4-turbo"] prompt_names = ["prompt_v1", "prompt_v2", "prompt_v3"] example_list = [["./examples/" + example] for example in os.listdir("examples")] +example_list_us = [["./examples_us/" + example] for example in os.listdir("examples_us")] +example_list_canada = [["./examples_canada/" + example] for example in os.listdir("examples_canada")] +example_france = [["./examples_france/" + example] for example in os.listdir("examples_france")] + prompt_default = read_prompt_from_file("prompt_v1.txt") system_instruction = read_prompt_from_file("system_instruction.txt") @@ -258,6 +262,19 @@ with gr.Blocks() as iface: ) prompt_radio.change(fn=update_prompt_from_radio, inputs=[prompt_radio], outputs=[custom_prompt]) gr.Examples(examples=example_list, - inputs=[image_input, model_radio, prompt_radio, temperature_slider, custom_prompt]) + inputs=[image_input, model_radio, prompt_radio, temperature_slider, custom_prompt], + label="Examples for general use") + + gr.Examples(examples=example_list_us, + inputs=[image_input, model_radio, prompt_radio, temperature_slider, custom_prompt], + label="Examples for US") + + gr.Examples(examples=example_list_canada, + inputs=[image_input, model_radio, prompt_radio, temperature_slider, custom_prompt], + label="Examples for Canada") + + gr.Examples(examples=example_france, + inputs=[image_input, model_radio, prompt_radio, temperature_slider, custom_prompt], + label="Examples for France") -iface.launch(share=True) +iface.launch() diff --git a/convert_to_webp.py b/convert_to_webp.py new file mode 100644 index 0000000000000000000000000000000000000000..3f4d07de19075b59e3b5990bb0a7c683840d3b79 --- /dev/null +++ b/convert_to_webp.py @@ -0,0 +1,30 @@ +from PIL import Image +import os + + +def convert_to_webp(input_file, output_file=None): + try: + img = Image.open(input_file) + if output_file is None: + base_name = os.path.splitext(input_file)[0] + output_file = f"{base_name}.webp" + + img.save(output_file, "WEBP") + print(f"saved as {output_file}") + + except Exception as e: + print(f"error saving: {e}") + + +def process_images_in_directory(directory): + print(directory) + for filename in os.listdir(directory): + if filename.lower().endswith(('.jpg', '.png', '.jpeg')): + input_path = os.path.join(directory, filename) + convert_to_webp(input_path) + +if __name__ == "__main__": + current_directory = os.getcwd() + print(f"current_directory: {current_directory}") + directory = "./examples_france/" + process_images_in_directory(directory) \ No newline at end of file diff --git a/examples/5404832557079585491.jpg b/examples/5404832557079585491.jpg deleted file mode 100644 index 29a4c9f6df83cdae600182e08f375ab4976343e8..0000000000000000000000000000000000000000 --- a/examples/5404832557079585491.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6b72846ffef2e01f5623af3bfa9e5087cca68d02699916a5f76eaba862838b56 -size 130390 diff --git a/examples/5404832557079585491.webp b/examples/5404832557079585491.webp new file mode 100644 index 0000000000000000000000000000000000000000..919350c11821661f2d5656a96d0729879e823a1f --- /dev/null +++ b/examples/5404832557079585491.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e760d186bc11ac0fd477832df93a2dcbcc930d19c44af4b640c25d48640a3617 +size 89702 diff --git a/examples/ATB_11.jpg b/examples/ATB_11.jpg deleted file mode 100644 index 4a815dafd166a6c1819c09a6761d1b251430895f..0000000000000000000000000000000000000000 --- a/examples/ATB_11.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:12fa36ea87d6f691b32d9f8c97a7c918a9a241fbb1e0c9bff875891b60f21789 -size 4733121 diff --git a/examples/ATB_11.webp b/examples/ATB_11.webp new file mode 100644 index 0000000000000000000000000000000000000000..a620b9a867d44bce778843735db3fd65a89f5403 --- /dev/null +++ b/examples/ATB_11.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2407761ef7df3019e8f43227414cf37f2f8b4cd70e77d8c0a4c9937021f34ced +size 1191646 diff --git a/examples/Sportisimo.jpg b/examples/Sportisimo.jpg deleted file mode 100644 index 6cb438e0323cf56160cb4ba5b4ba139502a3a670..0000000000000000000000000000000000000000 --- a/examples/Sportisimo.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:616dafff8b27152aa400beee8b6dc3dca09ddbfc97d79d097a2728a5b4376b2d -size 102161 diff --git a/examples/Sportisimo.webp b/examples/Sportisimo.webp new file mode 100644 index 0000000000000000000000000000000000000000..86fc76a93e919180514dd0f75dda14de7021e431 --- /dev/null +++ b/examples/Sportisimo.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58d2c09a47073d4f32ea12730e687f1b608ccbc52ba03dd2a5f64faa23e570f9 +size 53854 diff --git a/examples/cafe.png b/examples/cafe.png deleted file mode 100644 index 17001ce839d674bf9210debf108676ea92622c37..0000000000000000000000000000000000000000 --- a/examples/cafe.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:964ba9eb298527c7deff173b4a4608e8b01fbf9af8636bacd592b55354509d25 -size 1446948 diff --git a/examples/cafe.webp b/examples/cafe.webp new file mode 100644 index 0000000000000000000000000000000000000000..628c10211cd3c4c2b41cdf550c8043bae26302a8 --- /dev/null +++ b/examples/cafe.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07caf0b01f2c13e15e97f8358a325a3cdcaf18f34b685529d67217e6a0c0f893 +size 152874 diff --git a/examples/farmacy_1.jpg b/examples/farmacy_1.jpg deleted file mode 100644 index 2a8ace81af575c6f04348895758b165b43841061..0000000000000000000000000000000000000000 --- a/examples/farmacy_1.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f45b71446f6d9f3a32917c5f7e7fda30aa548e1220fcea33ba15dfde43476724 -size 120738 diff --git a/examples/farmacy_1.webp b/examples/farmacy_1.webp new file mode 100644 index 0000000000000000000000000000000000000000..385615c858d3128d188af1294e0b1cab89624b53 --- /dev/null +++ b/examples/farmacy_1.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66c81e0b4bb224c85a2c40ba099a766ee2f9f1b762f6a8e1538a0299f279eac1 +size 98124 diff --git a/examples/farmacy_2.jpg b/examples/farmacy_2.jpg deleted file mode 100644 index d40ff9d175f41a3702d044c3093b8a641fa96969..0000000000000000000000000000000000000000 --- a/examples/farmacy_2.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c197609b0e4409478b126c39f180540b1f96244613b65467d7a5caa114896ef6 -size 122097 diff --git a/examples/farmacy_2.webp b/examples/farmacy_2.webp new file mode 100644 index 0000000000000000000000000000000000000000..72842494c97d42a6defa21458378282448f93f20 --- /dev/null +++ b/examples/farmacy_2.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:378a4852aff0af61fe18d9282d9dadd95eea6f1672faf0534d16ac33762844ed +size 98980 diff --git a/examples/fatlouis.webp b/examples/fatlouis.webp new file mode 100644 index 0000000000000000000000000000000000000000..628c10211cd3c4c2b41cdf550c8043bae26302a8 --- /dev/null +++ b/examples/fatlouis.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07caf0b01f2c13e15e97f8358a325a3cdcaf18f34b685529d67217e6a0c0f893 +size 152874 diff --git a/examples/garm_3.JPG b/examples/garm_3.JPG deleted file mode 100644 index 4c3691030d49a9bbd3fd1b2aef10deec74e51b4b..0000000000000000000000000000000000000000 --- a/examples/garm_3.JPG +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c1444d4b5ae75ae1c2e70ab3de67d6505ef11c45e7f1be3faf1902e6474014d3 -size 2176517 diff --git a/examples/garm_3.webp b/examples/garm_3.webp new file mode 100644 index 0000000000000000000000000000000000000000..e9d96f704ee14765f1075e068e42297de1f5149d --- /dev/null +++ b/examples/garm_3.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:419abb20db12bd8885826d52ddb1fb9d0a0b897c9125e35eb11ed8b177d9157d +size 607968 diff --git a/examples/image_2024_09_25T12_13_01_811Z.png b/examples/image_2024_09_25T12_13_01_811Z.png deleted file mode 100644 index cec01416e73386d2ab25a8cf185e9545b2a7b1aa..0000000000000000000000000000000000000000 --- a/examples/image_2024_09_25T12_13_01_811Z.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e84d0fee6867825b325df2da52efb949406ef1d821923fd99fcad38c4dad92a8 -size 1361039 diff --git a/examples/image_2024_09_25T12_13_01_811Z.webp b/examples/image_2024_09_25T12_13_01_811Z.webp new file mode 100644 index 0000000000000000000000000000000000000000..d1894c3f635faf80eb7de4ce50a292683d633094 --- /dev/null +++ b/examples/image_2024_09_25T12_13_01_811Z.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c14d7d8549fedc90b2179df6ada2c27dd235c15754ab646ca2a1273e5906195 +size 89134 diff --git a/examples/lidl1.jpg b/examples/lidl1.jpg deleted file mode 100644 index d2402b84c3237c2642a5aa8e8a5c80666e49a57d..0000000000000000000000000000000000000000 --- a/examples/lidl1.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e13896a25ffee139c52a1be9cd0cf0525af45c56fb7aa21ca375206d3f2bc495 -size 100284 diff --git a/examples/lidl1.webp b/examples/lidl1.webp new file mode 100644 index 0000000000000000000000000000000000000000..13db394496fef02f3996c677302532ced69acea2 --- /dev/null +++ b/examples/lidl1.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f68d048dbf532c28e67f9ca18476109feb2733f586138234683a9045cad0e543 +size 52338 diff --git a/examples/lidl2.jpg b/examples/lidl2.jpg deleted file mode 100644 index 0741ae71f5096574f3d7fd2b97a16e15196b5bf1..0000000000000000000000000000000000000000 --- a/examples/lidl2.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9e0a8f8398bf9eca3d4fa5ba8f3b0b00cbd5b1f8278a24b77166302f3cb56453 -size 127181 diff --git a/examples/lidl2.webp b/examples/lidl2.webp new file mode 100644 index 0000000000000000000000000000000000000000..9fc592ffc113a1cfabd5a9922835ce66a5caea89 --- /dev/null +++ b/examples/lidl2.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3a70b5517303c3d0d703cc46afcdeac64d191ba9f393b543665feabad70b55f +size 69692 diff --git a/examples/photo_2024-09-10_10-06-14.jpg b/examples/photo_2024-09-10_10-06-14.jpg deleted file mode 100644 index 5a6acbdfc8ef8e4139e06e13fc4abb2ed89ed6ae..0000000000000000000000000000000000000000 --- a/examples/photo_2024-09-10_10-06-14.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3e6fc8e73ccbda7eb5c38ab55a9750624a4380a2716babb2e4ae7844ea30108c -size 159228 diff --git a/examples/photo_2024-09-10_10-06-14.webp b/examples/photo_2024-09-10_10-06-14.webp new file mode 100644 index 0000000000000000000000000000000000000000..34c75de80ae6d84db8c4fc0bd98a81e442dcee91 --- /dev/null +++ b/examples/photo_2024-09-10_10-06-14.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8692e1ad094ce9efcd45e74ddcf8f4867c6809b653f95abc86775efebfff22d +size 85424 diff --git a/examples/photo_2024-09-10_10-06-24.jpg b/examples/photo_2024-09-10_10-06-24.jpg deleted file mode 100644 index e2e6950eb813ff05d679e3a592fa7f298b950773..0000000000000000000000000000000000000000 --- a/examples/photo_2024-09-10_10-06-24.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4fe19064b828a47ef99ee9001f79e3fa8b597fd5f06d9c4bb098761a025fe1d6 -size 123627 diff --git a/examples/photo_2024-09-10_10-06-24.webp b/examples/photo_2024-09-10_10-06-24.webp new file mode 100644 index 0000000000000000000000000000000000000000..0eecbc5e0d7fdaafb1f66b91a51b67f4469322ef --- /dev/null +++ b/examples/photo_2024-09-10_10-06-24.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abd73a7185d3bf079071bada32756dd6bc58db4f8704d36452731b4c6fc17bf0 +size 59780 diff --git a/examples/photo_2024-09-10_10-06-28.jpg b/examples/photo_2024-09-10_10-06-28.jpg deleted file mode 100644 index d6d77fc4c4c94b24cde96182bc55125d9d882b63..0000000000000000000000000000000000000000 --- a/examples/photo_2024-09-10_10-06-28.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:078ec637f509a90c4f1a8b374eba218f093f3bfbba9314e12bec4a23a9217a38 -size 139562 diff --git a/examples/photo_2024-09-10_10-06-28.webp b/examples/photo_2024-09-10_10-06-28.webp new file mode 100644 index 0000000000000000000000000000000000000000..c50faf1ae28ed3711c3b62aa96f499690026892f --- /dev/null +++ b/examples/photo_2024-09-10_10-06-28.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f4c1bd4f2e2b11827c645349f9b74babd240b3226b3355c847f01de76d7034d +size 83292 diff --git a/examples/photo_2024-09-24_14-50-34.jpg b/examples/photo_2024-09-24_14-50-34.jpg deleted file mode 100644 index 4e426c5df8c25ce045275fc3e94f8060b3e1faf2..0000000000000000000000000000000000000000 --- a/examples/photo_2024-09-24_14-50-34.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:26f24a52d85b95b09c0770c9107f42878fb1298a6331c7cdd27ac8128625509c -size 245163 diff --git a/examples/photo_2024-09-24_14-50-34.webp b/examples/photo_2024-09-24_14-50-34.webp new file mode 100644 index 0000000000000000000000000000000000000000..b35b835ef39af7533c723e082854712fe9e395e3 --- /dev/null +++ b/examples/photo_2024-09-24_14-50-34.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4c982df0ac83dde7a6641ab25b47edf3fbedcf188f3125053bdb9172b35b25f +size 153862 diff --git a/examples/tiket.jpg b/examples/tiket.jpg deleted file mode 100644 index 12d18df06b38cb296db2a21b3645987b1ee81038..0000000000000000000000000000000000000000 --- a/examples/tiket.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aba99a5a1c193f00feb02048f590697629f91af5d64caeeab58303c5fa4807b6 -size 140500 diff --git a/examples/tiket.webp b/examples/tiket.webp new file mode 100644 index 0000000000000000000000000000000000000000..010214477384fe28d6113e297ae01c5770013c05 --- /dev/null +++ b/examples/tiket.webp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:386ed5c34b497ba0b693492c0051e63e4757337770800995be42807645239ce4 +size 112874 diff --git a/examples_canada/photo_2024-10-09_14-21-54.webp b/examples_canada/photo_2024-10-09_14-21-54.webp new file mode 100644 index 0000000000000000000000000000000000000000..3736bf922c1ea9515d2e533f1a889d7bf3ce11d6 Binary files /dev/null and b/examples_canada/photo_2024-10-09_14-21-54.webp differ diff --git a/examples_canada/photo_2024-10-09_14-23-03.webp b/examples_canada/photo_2024-10-09_14-23-03.webp new file mode 100644 index 0000000000000000000000000000000000000000..913582d6a2e37f4566b672084f4eaccf21836b36 Binary files /dev/null and b/examples_canada/photo_2024-10-09_14-23-03.webp differ diff --git a/examples_france/photo_1_2024-10-02_00-08-53.webp b/examples_france/photo_1_2024-10-02_00-08-53.webp new file mode 100644 index 0000000000000000000000000000000000000000..96946fb46538dc3fefea2f00c28780ce20743bf7 Binary files /dev/null and b/examples_france/photo_1_2024-10-02_00-08-53.webp differ diff --git a/examples_france/photo_2024-10-07_21-46-05.webp b/examples_france/photo_2024-10-07_21-46-05.webp new file mode 100644 index 0000000000000000000000000000000000000000..3789b8f7c736db39336997e27d58b27a7528a241 Binary files /dev/null and b/examples_france/photo_2024-10-07_21-46-05.webp differ diff --git a/examples_france/photo_2024-10-07_21-46-27.webp b/examples_france/photo_2024-10-07_21-46-27.webp new file mode 100644 index 0000000000000000000000000000000000000000..c2971e96cdfcf34ffee23e42dc3b8888a482c8bb Binary files /dev/null and b/examples_france/photo_2024-10-07_21-46-27.webp differ diff --git a/examples_france/photo_2_2024-10-02_00-08-53.webp b/examples_france/photo_2_2024-10-02_00-08-53.webp new file mode 100644 index 0000000000000000000000000000000000000000..c461654d42cd9472569ccdf18e2f5662b9898a49 Binary files /dev/null and b/examples_france/photo_2_2024-10-02_00-08-53.webp differ diff --git a/examples_france/photo_3_2024-10-02_00-08-53.webp b/examples_france/photo_3_2024-10-02_00-08-53.webp new file mode 100644 index 0000000000000000000000000000000000000000..57d504aff3acd0ab67479545eea25b51233d59fd Binary files /dev/null and b/examples_france/photo_3_2024-10-02_00-08-53.webp differ diff --git a/examples_france/photo_4_2024-10-02_00-08-53.webp b/examples_france/photo_4_2024-10-02_00-08-53.webp new file mode 100644 index 0000000000000000000000000000000000000000..b97a58f70149a086de4fb1409244fa641b8b4bce Binary files /dev/null and b/examples_france/photo_4_2024-10-02_00-08-53.webp differ diff --git a/examples_france/photo_5_2024-10-02_00-08-53.webp b/examples_france/photo_5_2024-10-02_00-08-53.webp new file mode 100644 index 0000000000000000000000000000000000000000..11d1d7f6d4c1e5003d0704eaf2f59a9936dd73e7 Binary files /dev/null and b/examples_france/photo_5_2024-10-02_00-08-53.webp differ diff --git a/examples_france/photo_6_2024-10-02_00-08-53.webp b/examples_france/photo_6_2024-10-02_00-08-53.webp new file mode 100644 index 0000000000000000000000000000000000000000..fe72c8d63bbdeb834ced45476506ee9e25ddc493 Binary files /dev/null and b/examples_france/photo_6_2024-10-02_00-08-53.webp differ diff --git a/examples_france/photo_7_2024-10-02_00-08-53.webp b/examples_france/photo_7_2024-10-02_00-08-53.webp new file mode 100644 index 0000000000000000000000000000000000000000..b4eb7a80557494c25b2efc8ca612983191c6d3da Binary files /dev/null and b/examples_france/photo_7_2024-10-02_00-08-53.webp differ diff --git a/examples_france/photo_8_2024-10-02_00-08-53.webp b/examples_france/photo_8_2024-10-02_00-08-53.webp new file mode 100644 index 0000000000000000000000000000000000000000..6ea26d45cacc144ea7f02fa7bd528a7931e9fb44 Binary files /dev/null and b/examples_france/photo_8_2024-10-02_00-08-53.webp differ diff --git a/examples_us/Photo Sep 29 2024, 15 43 17.webp b/examples_us/Photo Sep 29 2024, 15 43 17.webp new file mode 100644 index 0000000000000000000000000000000000000000..df778533c548e4582ea883b120dbd4d15e820fdc Binary files /dev/null and b/examples_us/Photo Sep 29 2024, 15 43 17.webp differ diff --git a/examples_us/Photo Sep 29 2024, 15 43 30.webp b/examples_us/Photo Sep 29 2024, 15 43 30.webp new file mode 100644 index 0000000000000000000000000000000000000000..fd280d475962a5f07d5782b26914db65b7d45ac4 Binary files /dev/null and b/examples_us/Photo Sep 29 2024, 15 43 30.webp differ diff --git a/examples_us/Photo Sep 29 2024, 15 43 48.webp b/examples_us/Photo Sep 29 2024, 15 43 48.webp new file mode 100644 index 0000000000000000000000000000000000000000..0d8dbc175addfc5f5a63c9e4eaaedf7428667260 Binary files /dev/null and b/examples_us/Photo Sep 29 2024, 15 43 48.webp differ diff --git a/examples_us/Photo Sep 29 2024, 15 44 21.webp b/examples_us/Photo Sep 29 2024, 15 44 21.webp new file mode 100644 index 0000000000000000000000000000000000000000..3d43cb14b5ce74e0aa7807c95b9e817ccc4e867b Binary files /dev/null and b/examples_us/Photo Sep 29 2024, 15 44 21.webp differ diff --git a/examples_us/Photo Sep 29 2024, 15 44 42.webp b/examples_us/Photo Sep 29 2024, 15 44 42.webp new file mode 100644 index 0000000000000000000000000000000000000000..639ff18b79e65a9e3893e3cb50ffd3303f39e6b0 Binary files /dev/null and b/examples_us/Photo Sep 29 2024, 15 44 42.webp differ diff --git a/examples_us/Photo Sep 29 2024, 15 45 13.webp b/examples_us/Photo Sep 29 2024, 15 45 13.webp new file mode 100644 index 0000000000000000000000000000000000000000..a32920b7932fcde60e5ff9d085a352fe4e1f70d1 Binary files /dev/null and b/examples_us/Photo Sep 29 2024, 15 45 13.webp differ diff --git a/examples_us/Photo Sep 29 2024, 15 45 26.webp b/examples_us/Photo Sep 29 2024, 15 45 26.webp new file mode 100644 index 0000000000000000000000000000000000000000..07057fb42c5ca4af1a7fbf0fed925028017b0998 Binary files /dev/null and b/examples_us/Photo Sep 29 2024, 15 45 26.webp differ diff --git a/examples_us/Photo Sep 29 2024, 15 55 11.webp b/examples_us/Photo Sep 29 2024, 15 55 11.webp new file mode 100644 index 0000000000000000000000000000000000000000..2eb27f4f2fd1bd47e86721c1e7590d7272396f08 Binary files /dev/null and b/examples_us/Photo Sep 29 2024, 15 55 11.webp differ diff --git a/examples_us/Photo Sep 29 2024, 15 55 49.webp b/examples_us/Photo Sep 29 2024, 15 55 49.webp new file mode 100644 index 0000000000000000000000000000000000000000..ef2c330022e6781f6b70dbc5578fa64b9b489e0e Binary files /dev/null and b/examples_us/Photo Sep 29 2024, 15 55 49.webp differ diff --git a/examples_us/Photo Sep 29 2024, 15 56 28.webp b/examples_us/Photo Sep 29 2024, 15 56 28.webp new file mode 100644 index 0000000000000000000000000000000000000000..fe25a2c61307cdbc9ed4a2a1dfa19b81bcac18ed Binary files /dev/null and b/examples_us/Photo Sep 29 2024, 15 56 28.webp differ diff --git a/google_drive_client.py b/google_drive_client.py index 96270a83765d5b481458b5272f04be68953e7734..da0220c886eeb4524c7a71c395b88f8af3c6802b 100644 --- a/google_drive_client.py +++ b/google_drive_client.py @@ -13,7 +13,7 @@ class GoogleDriveClient: :param json_key_path: Path to the JSON file (optional) :param json_key_env_var: Environment variable name containing the JSON key (default 'GOOGLE_SERVICE_ACCOUNT_KEY') """ - self.json_key_path = json_key_path or os.getenv('GOOGLE_VERTEX_KEY_PATH') + self.json_key_path = json_key_path self.json_key_env_var = json_key_env_var self.service = self._authenticate_google_drive() diff --git a/image_processing_interface.py b/image_processing_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..a89f606f33c87a3264bc5192fb4121b7cd8ddc66 --- /dev/null +++ b/image_processing_interface.py @@ -0,0 +1,7 @@ +from abc import ABC, abstractmethod + +class ImageProcessingInterface(ABC): + + @abstractmethod + def process_image(self, image_base64: str, model_name: str, prompt: str, system: str, temperature: float): + pass diff --git a/openai_service.py b/openai_service.py index be34143c5564dd7d891666a5173bcfdc62d6875d..f564abffaf2cab99a2bb2068b26d7f328404c2fa 100644 --- a/openai_service.py +++ b/openai_service.py @@ -1,63 +1,153 @@ import json -import base64 import os +import time + from openai import OpenAI -class OpenAIService: +from image_processing_interface import ImageProcessingInterface +from utils import encode_image_to_webp_base64 + + +class OpenAIService(ImageProcessingInterface): + _instance = None + + def __new__(cls, *args, **kwargs): + if cls._instance is None: + cls._instance = super(OpenAIService, cls).__new__(cls) + return cls._instance + def __init__(self, api_key=None): - self.api_key = api_key or os.environ.get("OPENAI_API_KEY") - if self.api_key: - print("OPENAI_API_KEY was found.") - else: - raise ValueError("OPENAI_API_KEY not found.") + if not hasattr(self, "_initialized"): + self.api_key = api_key or os.environ.get("OPENAI_API_KEY") + if self.api_key: + print("OPENAI_API_KEY was found.") + else: + raise ValueError("OPENAI_API_KEY not found.") - self.client = OpenAI(api_key=self.api_key) + self.client = OpenAI(api_key=self.api_key) + self._initialized = True - def process_image(self, input_image64, model_name, prompt, system = "You are receipt recognizer!", temperatura = 0.0): + def process_image(self, input_image64, model_name, prompt, system="You are a receipt recognizer!", temperature=0.0): if not input_image64: - return None, "No objects detected." + raise ValueError("No image provided.") + + try: + start_time = time.time() + + tools = [ + { + "type": "function", + "function": { + "name": "parse_image", + "description": "Parses receipt data from image into a structured JSON format.", + "parameters": { + "type": "object", + "properties": { + "store_name": {"type": "string"}, + "store_address": {"type": "string"}, + "date_time": {"type": "string"}, + "currency": {"type": "string"}, + "total_amount": {"type": "number"}, + "total_discount": {"type": "number"}, + "payment_method": { + "type": "string", + "enum": ["card", "cash", "unknown"] + }, + "rounding": {"type": "number"}, + "tax": {"type": "number"}, + "tips": {"type": "number"}, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "unit_price": {"type": "number"}, + "quantity": {"type": "number"}, + "unit_of_measurement": {"type": "string"}, + "total_price": {"type": "number"}, + "discount": {"type": "number"}, + "category": {"type": "string"} + }, + "required": ["name", "unit_price", "quantity", "total_price"] + } + } + }, + "required": ["store_name", "date_time", "total_price", "items"] + } + } + } + ] - print("Model name:", model_name) - print("system:", system) - print("Temperature:", temperatura) + response = self.client.chat.completions.create( + model=model_name, + messages=[ + {"role": "system", "content": system}, + {"role": "user", "content": [ + {"type": "text", "text": prompt}, + {"type": "image_url", "image_url": { + "url": f"data:image/webp;base64,{input_image64}"} + } + ]} + ], + tools=tools, + temperature=temperature, + response_format={"type": "json_object"} + ) - response = self.client.chat.completions.create( - model=model_name, - messages=[ - {"role": "system", "content": f"{system}"}, - {"role": "user", "content": [ - {"type": "text", "text": f"{prompt}"}, - {"type": "image_url", "image_url": { - "url": f"data:image/webp;base64,{input_image64}"} - } - ]} - ], - temperature=temperatura, - response_format={"type": "json_object"} - ) + # Print the raw response for debugging + print("Raw API Response:", response) - json_content = json.loads(response.choices[0].message.content) + end_time = time.time() + print(f"The OpenAIService spent {end_time - start_time:.2f} seconds.") - json_content['input_tokens'] = response.usage.prompt_tokens - json_content['output_tokens'] = response.usage.completion_tokens - json_content['total_tokens'] = response.usage.total_tokens + # Extract the function call result + if response.choices: + choice = response.choices[0] + if choice.message.tool_calls: + function_call = choice.message.tool_calls[0] + if function_call.function and function_call.function.arguments: + json_content = json.loads(function_call.function.arguments) - print(json_content) + # Add token usage information + json_content['input_tokens'] = response.usage.prompt_tokens + json_content['output_tokens'] = response.usage.completion_tokens + json_content['total_tokens'] = response.usage.total_tokens + json_content['time'] = end_time - start_time + + return json.dumps(json_content, indent=4) + else: + raise ValueError("No valid function call data found in the API response.") + elif choice.message.content: + raise ValueError(choice.message.content) + else: + raise ValueError("No tool calls found in the API response.") + else: + raise ValueError("The API response does not contain valid choices.") + + except Exception as e: + raise RuntimeError(f"Failed to process the image: {str(e)}") + + + + except Exception as e: + raise RuntimeError(f"Failed to process the image: {str(e)}") - return json.dumps(json_content, indent=4) if __name__ == "__main__": - processor = OpenAIService() + try: + processor = OpenAIService() + + # Image processing + image_path = "./examples/fatlouis.webp" + input_image64 = encode_image_to_webp_base64(image_path) - # Image processing - image_path = "./examples/lidl2.jpg" - with open(image_path, "rb") as image_file: - input_image64 = base64.b64encode(image_file.read()).decode('utf-8') + system = "You are a receipt recognizer." + with open('./prompt_v1.txt', 'r', encoding='utf-8') as file: + prompt = file.read() + result = processor.process_image(input_image64, "gpt-4o-mini", prompt, system, 0.0) - system = "You are receipt recognizer " - prompt = "Recognize the receipt and provide result as json " - result = processor.process_image(input_image64, "gpt-4o-mini", prompt, system, 0.0) - print(f'Image processing result: {result}') - decoded_string = result.encode('utf-8').decode('unicode_escape') + print(f'Image processing result: {result}') - print(result) + except Exception as e: + print(f"Error: {e}") diff --git a/process_images_gemini_multithread.py b/process_images_gemini_multithread.py index a450b88940e310d63a262d8b80d1fd03ef0dbff0..f7c3e174fde55c983b370c14f7a16d8ef3978baf 100644 --- a/process_images_gemini_multithread.py +++ b/process_images_gemini_multithread.py @@ -50,7 +50,7 @@ def process_folder_images(folder_path, json_key_path): client = VertexAIService(json_key_path=json_key_path) # Get a list of all images in the folder - image_files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if f.endswith(('.jpg', '.jpeg', '.png'))] + image_files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if f.endswith(('.jpg', '.jpeg', '.png','webp'))] # Use ThreadPoolExecutor for parallel processing with ThreadPoolExecutor() as executor: diff --git a/process_images_gemini_one_hread.py b/process_images_gemini_one_hread.py index 0c30fb4abc9a6efd34d0f684b894309c4cda5d7b..65743b10c6fed5efaea606b9370aca1b1b1c3ccb 100644 --- a/process_images_gemini_one_hread.py +++ b/process_images_gemini_one_hread.py @@ -46,7 +46,7 @@ def process_folder_images_sequentially(folder_path, json_key_path): client = VertexAIService(json_key_path=json_key_path) # Get a list of all images in the folder - image_files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if f.endswith(('.jpg', '.jpeg', '.png'))] + image_files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if f.endswith(('.jpg', '.jpeg', '.png', 'webp'))] # Process each image file sequentially for file_path in image_files: diff --git a/prompt_v1.txt b/prompt_v1.txt index bcda67f0181fd11c38117400f096ff9bf603488f..0b79cb579fed4ada12aa0c393618eb4df21bd169 100644 --- a/prompt_v1.txt +++ b/prompt_v1.txt @@ -1,49 +1,51 @@ -Task: Extract structured information from receipt images. -Receipts: May be in various languages, including non-Latin scripts, and have diverse formats. -Information to Extract: -- Store name -- Store address -- Currency (e.g., USD, EUR) -- date (in format "YYYY.MM.DD HH:MM:SS") -- tax -- Purchased items: - - Name - - Price per unit (format: 0.00) - - Quantity or weight - - unit of measurement (string) - - Total price (format: 0.00) - - Discount applied (if any, format: 0.00) - - Category (e.g., groceries, electronics, dining) — determine based on item name / description or context -- Total amount (format: 0.00) -- Total discount (format: 0.00) -- Tax -- Tips (if tips included in Total you need to write tips price, but if tips not included in Total you should write it's price as 0.00) -Output Format: JSON object with the following structure: +Task: Accurately extract structured information from receipt images and return it in a standardized JSON format. Ensure high accuracy even when receipts vary in format, language (including non-Latin scripts), and layout. Handle challenges like text noise, multiple lines for item names, and potential gaps in information. + +Receipts: May be in various languages (Latin and non-Latin scripts), in diverse formats, and may contain noise like logos, faded text, or watermarks. + +Output Format: Return the output as a JSON object with the following structure: + { - "store_name": "Store Name", - "store_address": "Store Address", - "currency": "Currency", - "date_time": "YYYY.MM.DD HH:MM:SS", - "payment_method": "card" or "cash - "items": [ - { - "name": "Item Name", - "unit_price": 0.00, - "quantity": 0, - "unit_of_measurement": - "total_price": 0.00, - "discount": 0.00, - "category": "Category Name" - } - ], - "total_amount": 0.00, - "total_discount": 0.00, - "tax": 0.00 - "tips": 0.00 + "store_name": string, -- Exact name of the store as found on the receipt. It`s not always the bigger text. Find the correct name of the shop/restaurant + "store_address": string, -- Full address, if available; otherwise, "unknown" + "date_time": "YYYY.MM.DD HH:MM:SS", -- Convert all date formats to this standard + "currency": string, -- Currency code (e.g., "EUR", "USD", "UAH") based on the detected currency symbol. Don`t put here currency symbol, only code. + "total_amount": 0.0, -- The final total amount from the receipt (in the majority of situations this one is bigger then other values + it could be as bold font). The total amount may not always be the largest number; ensure the context is understood from surrounding text. + "total_discount": 0.0, -- Total discount applied based on individual item discounts or explicit discount information + "payment_method": "card", "cash", or "unknown", -- Detect payment method based on keywords like "card", "cash", or if missing, use "unknown" + "rounding": 0.0, -- If rounding is not specified on the receipt, use 0.0 + "tax": 0.0, -- If tax is not found or mentioned, use 0.0 + "tips": 0.0, -- If tips is not found or mentioned, use 0.0 + "items": [ + { + "name": string, -- Full item name (even if it spans multiple lines) + "unit_price": 0.0, -- Price per unit, if available. If not, write here the same value as for total price + "quantity": 0.0, -- Quantity of the item, default 1.0 if it wasn`t written + "unit_of_measurement": string, -- Use the format "ks", "kg", etc. If not specified, default to "ks" + "total_price": 0.0, -- Total price for the item (quantity * unit price) + "discount": 0.0, -- If discount isn't listed, assume 0.0 + "category": string -- Category (e.g., dairy, beverages). Input there suitable category. Default to the most specific category possible, not general terms like "groceries". + } + ] } -If no receipt is detected: Return "Receipt not found." -Additional Notes: -1. If the receipt is in a non-Latin script, extract the information in its original form unless translation is required. -2. If any information is unclear or missing, include it as "unknown" or "not available" in the output. -Write whole json with information about all products. -3. Note that the name of an item may be a few lines long, but the item necessarily has a price in last line. \ No newline at end of file + +#Handling Item Price, Quantity, and Name Layout: +When processing items, note that the price, unit price, and quantity may appear above the item name. For example, if the receipt shows: +5 * 23.00 = 115.0 +Milk +Interpret this as: +Quantity: 5 +Unit Price: 23.00 +Item Name: Milk +Total Price: 115.0 + +#Additional Notes: +1. If no receipt is detected: Return "Receipt not found." +2. Handle various languages (including non-Latin scripts) and keep text in the original script unless translation is explicitly required. +3. If information is missing or unclear, return "unknown" or "not available" for that field. +4. Extract the full name of each item. Some items may have names split across multiple lines; in this case, concatenate the lines until you encounter a quantity or unit of measurement (e.g., "2ks"), which marks the end of the item name. +5. Some receipts could be, for example, from McDonald`s restaurant, where in receipts under menu name could be written components of this menu. In this case you should extract only menu name. +6. The total amount may not always be the largest number; ensure the context is understood from surrounding text. +7. Tips and Charity Donations: Extract and sum tips and charity donations, storing the total under the tips field. +8. Convert dates to the "YYYY.MM.DD HH:MM:SS" format, regardless of how they appear on the receipt (e.g., MM/DD/YY, DD-MM-YYYY). +9. Handle ambiguous data consistently. If there's ambiguity about price, quantity, or any other information, make the best effort to extract it, or return "unknown." +9. Be flexible in handling varied receipt layouts, item name formats, and currencies. \ No newline at end of file diff --git a/utils.py b/utils.py index 89f7bf9388bc3a382da40daf629b67c348af3b2d..5790dfdf377bd91d44b5d4a18eae58c01e845d5f 100644 --- a/utils.py +++ b/utils.py @@ -140,7 +140,7 @@ def process_receipt_json(json_input): for i in range(len(items_table)): items_table[i].append(total_product_prices[i].price) - if total_sum != round(float(str(total_amount).replace(",", ".")), 2): + if total_sum == "Not specified" or total_sum is None or total_sum != round(float(str(total_amount).replace(",", ".")), 2): message = "Recognized total sum and products total sum is not equal. Check if AI model correctly created a JSON" return store_info, items_table, message