implement photo cropping - simple bbox

This commit is contained in:
MarcZierle 2022-01-22 18:39:08 +01:00
parent a9c8e2cbda
commit 2fa9b70bc2
8 changed files with 136 additions and 29 deletions

View File

@ -88,4 +88,8 @@ export function updatePhoto(photo) {
export function deletePhoto(id) {
return apiClient.delete('/deletephoto/'+id+'/')
}
export function cropPhoto(id, mode) {
return apiClient.get('/cropphoto/'+id+'/?mode='+mode)
}

View File

@ -1,13 +1,28 @@
<template>
<div>
<cropper
class="cropper"
:src="src"
:stencil-props="{
aspectRatio: 1/1.41421356
}"
@change="change"
/>
<n-space justify="start">
<cropper
class="cropper"
:src="src"
:stencil-props="aspect_ratio"
:auto-zoom="true"
@change="change"
ref="cropper"
/>
<n-space vertical justify="space-between">
<div>
<h2>Preview</h2>
<n-image
class="preview"
height="200"
:src="preview_data_url"
/>
</div>
<n-checkbox v-model:checked="lock_aspect_ratio">lock aspect ratio to flip chart size?</n-checkbox>
</n-space>
</n-space>
</div>
</template>
@ -28,15 +43,48 @@ export default {
},
data() {
return {
preview_data_url: null,
lock_aspect_ratio: true,
bbox: []
}
},
mounted() {
console.log(this.src)
computed: {
aspect_ratio() {
if (this.lock_aspect_ratio) {
return {
aspectRatio: 1/1.41421356
}
}
return {}
}
},
methods: {
coordinates_to_bbox(coordinates) {
let top_left = [
coordinates.left,
coordinates.top
]
let top_right = [
coordinates.left + coordinates.width,
coordinates.top
]
let bottom_right = [
coordinates.left + coordinates.width,
coordinates.top + coordinates.height
]
let bottom_left = [
coordinates.left,
coordinates.top + coordinates.height
]
return [top_left, top_right, bottom_right, bottom_left]
},
change({ coordinates, canvas }) {
console.log(coordinates, canvas)
this.preview_data_url = canvas.toDataURL()
this.bbox = this.coordinates_to_bbox(coordinates)
},
getResult() {
return JSON.parse(JSON.stringify(this.bbox))
}
}
}
@ -48,4 +96,8 @@ export default {
width: 600px;
background: #DDD;
}
.preview {
max-width: 300px;
}
</style>

View File

@ -105,11 +105,16 @@ export default {
type: Number,
required: false,
default: 150,
}
},
init_selection: {
type: Boolean,
required: false,
default: false,
},
},
emits: [
'update:select',
'update:delet',
'update:delete',
'update:crop',
'update:ocr',
'update:group',
@ -126,6 +131,9 @@ export default {
beforeMount() {
this.imgSrcObj = new Image()
this.imgSrcObj.src = this.src
if (this.can_select && this.init_selection) {
this.selected = true
}
},
watch: {
imgSrcObj: {
@ -202,6 +210,7 @@ export default {
toggleSelection() {
if (this.can_select) {
this.selected = !this.selected
this.$emit('update:select', this.selected)
}
}
},

View File

@ -27,18 +27,18 @@
:title="group.name"
:name="group.id">
<n-image-group>
<n-space>
<n-image
<PhotoItem
v-for="photo in photos[group.id]"
:key="photo.id"
width="100"
:src="photo.cropped_image !== null ? photo.cropped_image : photo.original_image"
@click="toggle_select_photo(photo.id)"
:class="{selected: is_photo_selected(photo.id)}"
preview-disabled />
:can_select="true"
:init_selection="is_photo_selected(photo.id)"
@update:select="toggle_select_photo(photo.id)"
:ref="'photoitem-'+photo.id"
/>
</n-space>
</n-image-group>
<template #header-extra>{{group.date}}</template>
</n-collapse-item>
@ -57,13 +57,13 @@
<script>
import { useMessage } from 'naive-ui'
//import PhotoItem from '@/components/PhotoItem'
import PhotoItem from '@/components/PhotoItem'
export default {
name: 'PhotoSelectModal',
emits: [ 'closed', 'selected' ],
components: {
//PhotoItem
PhotoItem
},
props: {
showSelection: {
@ -140,6 +140,7 @@ export default {
if (this.selecions_left > 0) {
this.selection.push(photo_id)
} else {
this.$refs['photoitem-'+photo_id][0].selected = false
this.message.error('You can only select ' + this.max_select + ' photo(s)')
}
}

View File

@ -9,6 +9,7 @@ import {
getPhotosByGroup,
updatePhoto,
deletePhoto,
cropPhoto,
} from '@/api'
export default createStore({
@ -167,7 +168,20 @@ export default createStore({
reject(error)
})
})
}
},
cropPhoto({commit}, {id, mode}) {
new Promise((resolve, reject) => {
cropPhoto(id, mode).then((response) => {
commit('UPDATE_PHOTO', {
id: response.data.id,
cropped_image: response.data.cropped_image
})
resolve()
}).catch((error) => {
reject(error)
})
})
},
},
getters: {
photoLogById: (state) => (id) => {

View File

@ -16,7 +16,9 @@
<n-space justify="space-around" >
<n-space vertical>
<h2>Documents</h2>
<n-button type="primary">All Documents</n-button>
<a target="_blank" href='https://dev.marczierle.com/zierle-training/generate_document/modify_document.php?selection=0'>
<n-button type="primary">All Documents</n-button>
</a>
</n-space>
<n-space vertical>
<h2>Photo Logs</h2>

View File

@ -4,6 +4,7 @@
<n-table :bordered="false" :single-line="true">
<thead>
<th>Photo Log Title</th>
<th>PDF</th>
<th>Date created</th>
<th>delete</th>
</thead>
@ -12,6 +13,9 @@
<router-link :to="{name: 'CreateLog', params: {e: photolog.id}}">
<td>{{ photolog.title }}</td>
</router-link>
<td>
<a :href="photolog.pdf" target="_blank">PDF</a>
</td>
<td>{{ photolog.date }}</td>
<td>
<n-button type="error" @click="askDeleteLog(photolog.id)">Delete</n-button>

View File

@ -40,15 +40,17 @@
title="Adjust Photo Cropping"
:bordered="true"
>
<template #header-extra> Header </template>
<PhotoCropper :src="current_photo_src" />
<template #footer> Footer </template>
<PhotoCropper
style="max-width:900px;margin:auto;"
:src="current_photo_src"
ref="cropper"
/>
<template #action>
<n-space justify="end">
<n-button @click="showCropModal = false">Cancel</n-button>
<n-button type="primary">Save</n-button>
<n-button type="primary" @click="changeCrop">Save</n-button>
</n-space>
</template>
</n-card>
@ -242,6 +244,25 @@ export default {
})
}
},
changeCrop() {
let bbox = this.$refs.cropper.getResult()
if (bbox && this.current_photo) {
this.$store.dispatch('updatePhoto', {
id: this.current_photo,
bbox_coords: bbox
}).then(()=>{
setTimeout(()=>{
this.$store.dispatch('cropPhoto', {
id: this.current_photo,
mode: 'bbox'
}).then(()=>{
this.current_photo = null
this.showCropModal = false
})
}, 100)
})
}
},
changeOCRText(){
if (this.current_ocr_text.length !== null && this.current_photo) {
this.$store.dispatch('updatePhoto', {