Skip to content

Commit

Permalink
refactor(ContributionAssistant): var names, translations, avoid async
Browse files Browse the repository at this point in the history
  • Loading branch information
TTalex committed Dec 14, 2024
1 parent 95c664f commit 5738534
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 159 deletions.
38 changes: 0 additions & 38 deletions src/components/ContributionAssistantCropImageList.vue

This file was deleted.

79 changes: 41 additions & 38 deletions src/components/ContributionAssistantDrawCanvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,42 +19,42 @@
type: Image,
default: null
},
seedCrops: {
boundingBoxesFromServer: {
type: Array,
default: null
}
},
emits: ['croppedImages', 'loaded'],
emits: ['extractedLabels', 'loaded'],
data() {
return {
isDrawing: false,
startX: 0,
startY: 0,
scale: 1,
rectangles: []
boundingBoxes: []
}
},
watch: {
seedCrops() {
if (this.seedCrops) {
this.init(true)
boundingBoxesFromServer() {
if (this.boundingBoxesFromServer) {
this.initCanvas(true)
}
}
},
mounted() {
if (this.image.complete) {
this.init()
this.initCanvas()
} else {
this.image.onload = this.init
this.image.onload = this.initCanvas
}
},
methods: {
init(keepRectangles=false) {
initCanvas(keepBoundingBoxes=false) {
const canvas = this.$refs.canvas
const ctx = canvas.getContext("2d")
canvas.style.width = "100%"
this.scale = canvas.offsetWidth / this.image.width
const preferedHeight = 400
const preferedHeight = window.innerHeight - 250
if (preferedHeight < this.image.height * this.scale) {
// Image will be too tall
Expand All @@ -70,21 +70,22 @@
ctx.drawImage(this.image, 0, 0, newWidth, newHeight)
if (!keepRectangles) {
this.rectangles = [] // reset rectangles
if (!keepBoundingBoxes) {
this.boundingBoxes = [] // reset boundingBoxes
}
if (this.seedCrops) {
this.rectangles = this.rectangles.concat(this.seedCrops.map(seedCrop => {
if (this.boundingBoxesFromServer) {
this.boundingBoxes = this.boundingBoxes.concat(this.boundingBoxesFromServer.map(boundingBox => {
return {
startY: seedCrop[0] * this.image.height,
startX: seedCrop[1] * this.image.width,
endY: seedCrop[2] * this.image.height,
endX: seedCrop[3] * this.image.width
startY: boundingBox[0] * this.image.height,
startX: boundingBox[1] * this.image.width,
endY: boundingBox[2] * this.image.height,
endX: boundingBox[3] * this.image.width,
boundingSource: this.$t('ContributionAssistant.AutomaticBoundingBoxSource')
}
}))
this.cropImages()
this.extractLabels()
}
this.drawRectangles(); // Draw previous rectangles after resizing
this.drawBoundingBoxes(); // Draw previous boundingBoxes after resizing
this.$emit('loaded')
},
startDrawing(event) {
Expand All @@ -109,7 +110,7 @@
const ctx = canvas.getContext("2d")
ctx.drawImage(this.image, 0, 0, canvas.width, canvas.height); // Redraw image
this.drawRectangles(); // Redraw previous rectangles
this.drawBoundingBoxes(); // Redraw previous boundingBoxes
const currentX = event.offsetX / this.scale
const currentY = event.offsetY / this.scale
Expand All @@ -129,47 +130,49 @@
}
const endX = event.offsetX / this.scale
const endY = event.offsetY / this.scale
this.rectangles.push({ startX: this.startX, startY: this.startY, endX, endY })
this.cropImages()
this.boundingBoxes.push({ startX: this.startX, startY: this.startY, endX, endY, boundingSource: this.$t('ContributionAssistant.ManualBoundingBoxSource') })
this.extractLabels()
},
drawRectangles() {
drawBoundingBoxes() {
const ctx = this.$refs.canvas.getContext("2d")
ctx.strokeStyle = "red"
this.rectangles.forEach(rect => {
this.boundingBoxes.forEach(rect => {
const { startX, startY, endX, endY } = rect
const width = endX - startX
const height = endY - startY
ctx.strokeRect(startX * this.scale, startY * this.scale, width * this.scale, height * this.scale)
});
},
async cropImages() {
let croppedImages = []
let croppedBlobs = []
async extractLabels() {
let extractedLabels = []
const originalCanvas = document.createElement("canvas")
const ctx = originalCanvas.getContext("2d")
for (let i = 0; i < this.rectangles.length; i++) {
const rect = this.rectangles[i]
const { startX, startY, endX, endY } = rect
for (let i = 0; i < this.boundingBoxes.length; i++) {
const rect = this.boundingBoxes[i]
const { startX, startY, endX, endY, boundingSource } = rect
const width = Math.abs(endX - startX)
const height = Math.abs(endY - startY)
originalCanvas.width = width
originalCanvas.height = height
ctx.drawImage(this.image, Math.min(startX, endX), Math.min(startY, endY), width, height, 0, 0, width, height)
croppedImages[i] = originalCanvas.toDataURL()
croppedBlobs[i] = await new Promise(resolve => originalCanvas.toBlob(resolve, 'image/webp'))
extractedLabels[i] = {
imageSrc: originalCanvas.toDataURL(),
blob: await new Promise(resolve => originalCanvas.toBlob(resolve, 'image/webp')),
boundingSource: boundingSource
}
}
this.$emit('croppedImages', [croppedImages, croppedBlobs])
this.$emit('extractedLabels', extractedLabels)
},
removeRectangle(index) {
this.rectangles.splice(index, 1)
removeBoundingBox(index) {
this.boundingBoxes.splice(index, 1)
const canvas = this.$refs.canvas
const ctx = canvas.getContext("2d")
ctx.drawImage(this.image, 0, 0, canvas.width, canvas.height)
this.drawRectangles()
this.cropImages()
this.drawBoundingBoxes()
this.extractLabels()
}
}
}
Expand Down
57 changes: 57 additions & 0 deletions src/components/ContributionAssistantLabelList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<template>
<v-row v-if="labels.length">
<v-col
v-for="(label, index) in labels"
:key="index"
cols="6"
md="6"
xl="4"
>
<v-card class="pa-2">
<v-card-text>
<v-img style="height:150px" :src="label.imageSrc" />
</v-card-text>
<v-divider />
<v-card-actions>
<v-list-item class="w-100">
<template #prepend>
<v-chip label size="small" density="comfortable">
<span>
{{ $t('Common.Source') }} {{ label.boundingSource }}
</span>
</v-chip>
</template>
<template #append>
<div class="justify-self-end">
<v-btn
density="compact"
color="error"
@click="removeLabel(index)"
>
{{ $t('Common.Delete') }}
</v-btn>
</div>
</template>
</v-list-item>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</template>

<script>
export default {
props: {
labels: {
type: Array,
default: () => []
}
},
emits: ['removeLabel'],
methods: {
removeLabel(index) {
this.$emit('removeLabel', index)
}
}
}
</script>
4 changes: 2 additions & 2 deletions src/components/ContributionAssistantPriceFormCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
contain
/>
<v-card-text>
<ProductInputRow :productForm="productPriceForm" :disableInitWhenSwitchingModes="true" @filled="productFormFilled = $event" />
<ProductInputRow :productForm="productPriceForm" :disableInitWhenSwitchingType="true" @filled="productFormFilled = $event" />
<v-alert
v-if="productPriceForm.type === 'PRODUCT'"
class="mb-2"
type="info"
variant="plain"
>
Detected barcode: {{ productPriceForm.detected_product_code }}
{{ $t('ContributionAssistant.DetectedBarcode', { barcode: productPriceForm.detected_product_code }) }}
</v-alert>
<v-row v-if="productPriceForm.type == 'CATEGORY'">
<v-col>
Expand Down
27 changes: 27 additions & 0 deletions src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,33 @@
"PetFood": "Pet food",
"Beauty": "Beauty"
},
"ContributionAssistant": {
"Steps": {
"ProofSelect": "1. Proof selection",
"LabelsExtraction": "2. Labels extraction",
"Cleanup": "3. Cleanup",
"Summary": "4. Summary"
},
"BoundingBoxesFromServerWarning": "No labels could be automatically detected. Please manually draw squares around the labels or press the button below to try again.",
"FindBoundingBoxes": "Automatically find labels",
"FindBoundingBoxesRunning": "Automatic label detection is running. Please wait...",
"LabelsExtractionSteps": {
"DrawBoundingBoxes": "1. Draw squares around the labels",
"CheckLabels": "2. Check the readability of labels",
"SendLabels": "3. Send the labels for automatic processing",
"SendLabelsButton": "Send labels"
},
"PriceAddConfirmationMessage": "{numberOfPricesAdded} price(s) will be added to an existing proof on the {date} at {locationName}",
"PriceAddProgress": "{numberOfPricesAdded} / {totalNumberOfPrices} prices added",
"WaitForUpload": "Please wait for upload",
"GoToDashboard": "Go to your dashboard",
"GoToProof": "Go to proof",
"AddNewProof": "Add a new proof",
"AutomaticBoundingBoxSource": "automatic",
"ManualBoundingBoxSource": "manual",
"DetectedBarcode": "Detected barcode: {barcode}",
"LabelProcessingErrorMessage": "Error: label automatic processing failed"
},
"Community": {
"JoinUs": "Join us!",
"HowToUseTheData": "Use the data"
Expand Down
6 changes: 3 additions & 3 deletions src/services/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,13 +336,13 @@ export default {
return this.openstreetmapNominatimSearch(q)
}
},
processWithGemini(croppedBlobs) {
processWithGemini(labels) {
const store = useAppStore()
const url = `${import.meta.env.VITE_OPEN_PRICES_API_URL}/proofs/process_with_gemini`
const formData = new FormData()

croppedBlobs.forEach((blob) => {
formData.append('files', blob)
labels.forEach((label) => {
formData.append('files', label.blob)
});

return fetch(url, {
Expand Down
Loading

0 comments on commit 5738534

Please sign in to comment.