Parcourir la source

Merge branch 'feature/first-experiment' into develop

rigwild il y a 5 ans
Parent
commit
f378ab4184

+ 27 - 3
server/routes/getImage.js

@@ -2,6 +2,7 @@
 
 
 import express from 'express'
 import express from 'express'
 import path from 'path'
 import path from 'path'
+import sharp from 'sharp'
 import boom from '@hapi/boom'
 import boom from '@hapi/boom'
 
 
 import { imagesPath, imageServedUrl } from '../../config'
 import { imagesPath, imageServedUrl } from '../../config'
@@ -30,6 +31,7 @@ const router = express.Router()
  * @apiSuccess {String} data.sceneName Scene name of the image
  * @apiSuccess {String} data.sceneName Scene name of the image
  * @apiSuccess {Number} data.quality Quality of the image
  * @apiSuccess {Number} data.quality Quality of the image
  * @apiSuccess {String} data.ext Extension of the image
  * @apiSuccess {String} data.ext Extension of the image
+ * @apiSuccess {Object} data.metadata Metadata of the image, @see https://sharp.dimens.io/en/stable/api-input/#metadata
  * @apiSuccessExample {json} Success response example
  * @apiSuccessExample {json} Success response example
  * HTTP/1.1 200 OK /api/getImage?sceneName=bathroom&imageQuality=200
  * HTTP/1.1 200 OK /api/getImage?sceneName=bathroom&imageQuality=200
  * {
  * {
@@ -38,7 +40,19 @@ const router = express.Router()
  *     "fileName": "bathroom_00200.png",
  *     "fileName": "bathroom_00200.png",
  *     "sceneName": "bathroom",
  *     "sceneName": "bathroom",
  *     "quality": 200,
  *     "quality": 200,
- *     "ext": "png"
+ *     "ext": "png",
+ *     "metadata": {
+ *       "format": "png",
+ *        "width": 800,
+ *        "height": 800,
+ *        "space": "rgb16",
+ *        "channels": 3,
+ *        "depth": "ushort",
+ *        "density": 72,
+ *        "isProgressive": false,
+ *        "hasProfile": false,
+ *        "hasAlpha": false
+ *      }
  *   }
  *   }
  * }
  * }
  *
  *
@@ -143,8 +157,10 @@ export const getImage = async (sceneName, quality, nearestQuality = false) => {
     else imageData = sceneData.find(x => quality === x.quality)
     else imageData = sceneData.find(x => quality === x.quality)
   }
   }
 
 
-  if (imageData)
-    return {
+
+  if (imageData) {
+    // Data gathered from file system
+    const result = {
       link: `${imageServedUrl}/${sceneName}/${imageData.fileName}`,
       link: `${imageServedUrl}/${sceneName}/${imageData.fileName}`,
       path: path.resolve(imagesPath, sceneName, imageData.fileName),
       path: path.resolve(imagesPath, sceneName, imageData.fileName),
       fileName: imageData.fileName,
       fileName: imageData.fileName,
@@ -153,6 +169,14 @@ export const getImage = async (sceneName, quality, nearestQuality = false) => {
       ext: imageData.ext
       ext: imageData.ext
     }
     }
 
 
+    // Data gathered by analysing the image
+    const input = sharp(result.path)
+    const metadata = await input.metadata()
+    result.metadata = metadata
+
+    return result
+  }
+
   // Image not found
   // Image not found
   throw boom.notFound(`The requested quality "${quality}" was not found for the requested scene "${sceneName}".`)
   throw boom.notFound(`The requested quality "${quality}" was not found for the requested scene "${sceneName}".`)
 }
 }

+ 50 - 7
server/routes/getImageExtracts.js

@@ -37,6 +37,13 @@ const router = express.Router()
  * @apiSuccess {String} data.info.sceneName Scene name of the original image
  * @apiSuccess {String} data.info.sceneName Scene name of the original image
  * @apiSuccess {Number} data.info.quality Quality of the original image
  * @apiSuccess {Number} data.info.quality Quality of the original image
  * @apiSuccess {String} data.info.ext Extension of the original image
  * @apiSuccess {String} data.info.ext Extension of the original image
+ * @apiSuccess {Object} data.metadata Metadata of the image, @see https://sharp.dimens.io/en/stable/api-input/#metadata
+ * @apiSuccess {Object} data.info.extractsConfig Configuration used to cut the image
+ * @apiSuccess {Number} data.info.extractsConfig.x Number of extracts per line (horizontal)
+ * @apiSuccess {Number} data.info.extractsConfig.y Number of extracts per row (vertical)
+ * @apiSuccess {Object} data.info.extractsSize Size of extracted images
+ * @apiSuccess {Number} data.info.extractsSize.width Width of the extracted images
+ * @apiSuccess {Number} data.info.extractsSize.height Height of the extracted images
  * @apiSuccessExample {json} Success response example
  * @apiSuccessExample {json} Success response example
  * HTTP/1.1 200 OK /api/getImageExtracts?sceneName=bathroom&imageQuality=200&horizontalExtractCount=1&verticalExtractCount=2
  * HTTP/1.1 200 OK /api/getImageExtracts?sceneName=bathroom&imageQuality=200&horizontalExtractCount=1&verticalExtractCount=2
  * {
  * {
@@ -46,11 +53,33 @@ const router = express.Router()
  *       "/api/images/bathroom/extracts/x1_y2/zone00002/bathroom_zone00002_200.png"
  *       "/api/images/bathroom/extracts/x1_y2/zone00002/bathroom_zone00002_200.png"
  *     ],
  *     ],
  *     "info": {
  *     "info": {
- *       "link": "/api/images/bathroom/bathroom_00200.png",
- *       "fileName": "bathroom_00200.png",
- *       "sceneName": "bathroom",
- *       "quality": 200,
- *       "ext": "png"
+ *       "extractsConfig": {
+ *         "x": 1,
+ *         "y": 2
+ *       },
+ *       "extractsSize": {
+ *         "width": 800,
+ *         "height": 400
+ *       },
+ *       "image": {
+ *         "link": "/api/images/bathroom/bathroom_00200.png",
+ *         "fileName": "bathroom_00200.png",
+ *         "sceneName": "bathroom",
+ *         "quality": 200,
+ *         "ext": "png"
+ *         "metadata": {
+ *           "format": "png",
+ *            "width": 800,
+ *            "height": 800,
+ *            "space": "rgb16",
+ *            "channels": 3,
+ *            "depth": "ushort",
+ *            "density": 72,
+ *            "isProgressive": false,
+ *            "hasProfile": false,
+ *            "hasAlpha": false
+ *          }
+ *       }
  *     }
  *     }
  *   }
  *   }
  * }
  * }
@@ -169,7 +198,11 @@ const cutImage = async (image, xExtracts, yExtracts) => {
         link: extractLink,
         link: extractLink,
         path: extractPath,
         path: extractPath,
         fileName: extractName,
         fileName: extractName,
-        sceneName: image.sceneName
+        sceneName: image.sceneName,
+        originalWidth: width,
+        originalHeight: height,
+        width: xCropSize,
+        height: yCropSize
       }
       }
 
 
       // Check the file already exist
       // Check the file already exist
@@ -273,7 +306,17 @@ router.get('/', asyncMiddleware(async (req, res) => {
   res.json({
   res.json({
     data: {
     data: {
       extracts: extracts.map(x => x.link),
       extracts: extracts.map(x => x.link),
-      info: image
+      info: {
+        extractsConfig: {
+          x: horizontalExtractCountInt,
+          y: verticalExtractCountInt
+        },
+        extractsSize: {
+          width: extracts[0].width,
+          height: extracts[0].height
+        },
+        image
+      }
     }
     }
   })
   })
 }))
 }))

+ 2 - 1
src/App.vue

@@ -116,6 +116,7 @@ export default {
         await fn()
         await fn()
       }
       }
       catch (err) {
       catch (err) {
+        console.error(err)
         this.loadingErrorMessage = err.message
         this.loadingErrorMessage = err.message
         return
         return
       }
       }
@@ -137,7 +138,7 @@ export default {
 
 
 <style scoped>
 <style scoped>
 .reset-button {
 .reset-button {
-  position: absolute;
+  position: fixed;
   right: 0;
   right: 0;
   bottom: 0;
   bottom: 0;
   z-index: 999;
   z-index: 999;

+ 5 - 1
src/components/HostConfig.vue

@@ -79,6 +79,10 @@ export default {
     }
     }
   },
   },
 
 
+  mounted() {
+    this.$router.push('/')
+  },
+
   methods: {
   methods: {
     ...mapActions(['setHostConfig']),
     ...mapActions(['setHostConfig']),
     reset() {
     reset() {
@@ -97,8 +101,8 @@ export default {
         await this.setHostConfig(this.config)
         await this.setHostConfig(this.config)
       }
       }
       catch (err) {
       catch (err) {
-        this.configErrorMessage = err.message
         console.error(err)
         console.error(err)
+        this.configErrorMessage = err.message
         return
         return
       }
       }
       finally {
       finally {

+ 3 - 2
src/components/Loader.vue

@@ -7,7 +7,7 @@
           color="primary"
           color="primary"
           indeterminate
           indeterminate
         />
         />
-        <div class="mt-3">{{ message }}</div>
+        <div class="mt-3" v-if="message">{{ message }}</div>
       </v-layout>
       </v-layout>
     </v-container>
     </v-container>
   </v-content>
   </v-content>
@@ -19,7 +19,8 @@ export default {
   props: {
   props: {
     message: {
     message: {
       type: String,
       type: String,
-      default: 'Loading...'
+      default: null,
+      required: false
     }
     }
   }
   }
 }
 }

+ 2 - 2
src/components/ResetAppButton.vue

@@ -115,8 +115,8 @@ export default {
         this.showDialog = false
         this.showDialog = false
       }
       }
       catch (err) {
       catch (err) {
-        console.error('Error while resetting the app', err)
-        this.$refs.toast.show('Error while resetting the app. ' + err.message, 'error', 10000)
+        console.error('Failed to reset the app', err)
+        this.$refs.toast.show('Failed to reset the app. ' + err.message, 'error', 10000)
       }
       }
       this.$router.push('/')
       this.$router.push('/')
     }
     }

+ 22 - 2
src/functions.js

@@ -4,12 +4,12 @@ export const API_ROUTES = {
 
 
   listScenes: () => `${API_PREFIX}/listScenes`,
   listScenes: () => `${API_PREFIX}/listScenes`,
 
 
-  listSceneQualities: sceneName => `${API_PREFIX}/listSceneQualities?sceneName=${new URLSearchParams({ sceneName })}`,
+  listSceneQualities: sceneName => `${API_PREFIX}/listSceneQualities?${new URLSearchParams({ sceneName })}`,
 
 
   getImage: (sceneName, imageQuality, nearestQuality = false) => `${API_PREFIX}/getImage?${new URLSearchParams({ sceneName, imageQuality, nearestQuality })}`,
   getImage: (sceneName, imageQuality, nearestQuality = false) => `${API_PREFIX}/getImage?${new URLSearchParams({ sceneName, imageQuality, nearestQuality })}`,
 
 
   getImageExtracts: (sceneName, imageQuality, horizontalExtractCount, verticalExtractCount, nearestQuality = false) =>
   getImageExtracts: (sceneName, imageQuality, horizontalExtractCount, verticalExtractCount, nearestQuality = false) =>
-    `${API_PREFIX}/getImage?${new URLSearchParams({
+    `${API_PREFIX}/getImageExtracts?${new URLSearchParams({
       sceneName,
       sceneName,
       imageQuality,
       imageQuality,
       horizontalExtractCount,
       horizontalExtractCount,
@@ -22,3 +22,23 @@ export const delay = ms => new Promise(res => setTimeout(res, ms))
 
 
 export const buildURI = (ssl, host, port, route = '') => `${ssl ? 'https' : 'http'}://${host}:${port}${route}`
 export const buildURI = (ssl, host, port, route = '') => `${ssl ? 'https' : 'http'}://${host}:${port}${route}`
 export const buildWsURI = (ssl, host, port, uuid = '') => `${ssl ? 'wss' : 'ws'}://${host}:${port}?uuid=${uuid}`
 export const buildWsURI = (ssl, host, port, uuid = '') => `${ssl ? 'wss' : 'ws'}://${host}:${port}?uuid=${uuid}`
+
+export const sortIntArray = intArray => intArray.sort((a, b) => a - b)
+
+export const findNearestUpper = (value, arrInt) => {
+  const arr = sortIntArray(arrInt)
+  const index = arr.findIndex(x => value === x)
+  if (index >= 0 && index <= arr.length - 1)
+    return index === arr.length - 1
+      ? arr[index]
+      : arr[index + 1]
+}
+
+export const findNearestLower = (value, arrInt) => {
+  const arr = sortIntArray(arrInt)
+  const index = arr.findIndex(x => value === x)
+  if (index >= 0 && index <= arr.length - 1)
+    return index === 0
+      ? arr[index]
+      : arr[index - 1]
+}

+ 4 - 0
src/store/getters.js

@@ -2,19 +2,23 @@ import { buildURI, buildWsURI } from '../functions'
 
 
 export default {
 export default {
   isHostConfigured(state) {
   isHostConfigured(state) {
+    if (!state) return
     return !!(state.hostConfig.ssl !== null && state.hostConfig.host && state.hostConfig.port)
     return !!(state.hostConfig.ssl !== null && state.hostConfig.host && state.hostConfig.port)
   },
   },
   getHostURI(state, getters) {
   getHostURI(state, getters) {
+    if (!state) return
     if (getters.isHostConfigured)
     if (getters.isHostConfigured)
       return buildURI(state.hostConfig.ssl, state.hostConfig.host, state.hostConfig.port)
       return buildURI(state.hostConfig.ssl, state.hostConfig.host, state.hostConfig.port)
   },
   },
 
 
   getHostWsURI(state, getters) {
   getHostWsURI(state, getters) {
+    if (!state) return
     if (getters.isHostConfigured)
     if (getters.isHostConfigured)
       return buildWsURI(state.hostConfig.ssl, state.hostConfig.host, state.hostConfig.port, state.uuid)
       return buildWsURI(state.hostConfig.ssl, state.hostConfig.host, state.hostConfig.port, state.uuid)
   },
   },
 
 
   areScenesLoaded(state) {
   areScenesLoaded(state) {
+    if (!state) return
     return state.scenesList !== null
     return state.scenesList !== null
   }
   }
 
 

+ 2 - 0
src/store/mutations.js

@@ -54,6 +54,8 @@ export default {
   },
   },
 
 
   SOCKET_ONOPEN(state, event) {
   SOCKET_ONOPEN(state, event) {
+    if (event === null) return
+
     console.info('Connected to WebSocket server')
     console.info('Connected to WebSocket server')
     Vue.prototype.$socket = event.currentTarget
     Vue.prototype.$socket = event.currentTarget
     state.socket.isConnected = true
     state.socket.isConnected = true

+ 236 - 15
src/views/Experiments/WithReference.vue

@@ -2,44 +2,265 @@
   <div>
   <div>
     <v-container grid-list-md text-xs-center fluid>
     <v-container grid-list-md text-xs-center fluid>
       <v-layout row wrap>
       <v-layout row wrap>
-        <v-flex xs6>
-          <v-card dark color="primary">
-            <v-card-text class="px-0">Experience image</v-card-text>
-            <v-img src="https://diran.univ-littoral.fr/api/images/Appart1opt02/appartAopt_00900.png" />
-          </v-card>
-        </v-flex>
-        <v-flex xs6>
-          <v-card dark color="primary">
-            <v-card-text>Reference image</v-card-text>
-            <v-img src="https://diran.univ-littoral.fr/api/images/Appart1opt02/appartAopt_00900.png" />
+        <v-flex xs12>
+          <h1>Experiment with reference</h1>
+          <v-card dark>
+            <v-container grid-list-sm fluid>
+              <v-layout row wrap>
+                <v-flex
+                  xs12
+                >
+                  <h1>Configuration</h1>
+                  <v-card-text class="px-0">Extracts per line (horizontal)</v-card-text>
+                  <v-slider
+                    v-model="experimentConfig.x"
+                    always-dirty
+                    persistent-hint
+                    thumb-label="always"
+                    min="1"
+                    max="25"
+                  />
+
+                  <v-card-text class="px-0">Extracts per row (vertical)</v-card-text>
+                  <v-slider
+                    v-model="experimentConfig.y"
+                    always-dirty
+                    persistent-hint
+                    thumb-label="always"
+                    min="1"
+                    max="25"
+                  />
+
+                  <v-btn @click="setConfig" :disabled="!isConfigNew">Confirm</v-btn>
+
+                  <v-alert v-if="loadingErrorMessage" :value="true" type="error" v-text="loadingErrorMessage" />
+                </v-flex>
+              </v-layout>
+            </v-container>
           </v-card>
           </v-card>
         </v-flex>
         </v-flex>
+        <!-- Loading screen -->
+        <loader v-if="loadingMessage" :message="loadingMessage" />
+        <!--/ Loading screen -->
+
+        <!-- Experiment -->
+        <template v-else-if="!loadingErrorMessage">
+          <v-flex xs12 sm6>
+            <v-card dark color="primary">
+              <v-card-text class="px-0">Experiment image</v-card-text>
+
+              <v-container class="pa-1">
+                <template v-for="i in extractConfig.x">
+                  <v-layout row wrap :key="`row-${i}`">
+                    <v-flex
+                      v-for="(anExtract, index) in extracts.slice(extractConfig.x * (i - 1), (extractConfig.x * i))"
+                      :key="`extract-${i}-${extractConfig.x}-${extractConfig.y}-${index}-${anExtract.quality}`"
+                      class="pa-0"
+                    >
+                      <v-card flat tile class="d-flex height100">
+                        <div
+                          v-if="anExtract.loading"
+                          class="img-loader"
+                          @click.right.prevent
+                        >
+                          <v-progress-circular
+                            :indeterminate="true"
+                          />
+                        </div>
+                        <v-img
+                          v-else
+                          :src="anExtract.link"
+                          @click.left.prevent="extractAction($event, anExtract)"
+                          @click.right.prevent="extractAction($event, anExtract)"
+                          class="cursor extract"
+                        >
+                          <template v-slot:placeholder>
+                            <v-layout
+                              fill-height
+                              align-center
+                              justify-center
+                              ma-0
+                            >
+                              <v-progress-circular indeterminate color="grey lighten-5" />
+                            </v-layout>
+                          </template>
+                        </v-img>
+                      </v-card>
+                    </v-flex>
+                  </v-layout>
+                </template>
+              </v-container>
+            </v-card>
+          </v-flex>
+          <v-flex sm6 xs12>
+            <v-card dark color="primary">
+              <v-card-text>Reference image</v-card-text>
+              <v-img v-if="referenceImage" :src="referenceImage" />
+            </v-card>
+          </v-flex>
+        </template>
+      <!--/ Experiment -->
       </v-layout>
       </v-layout>
     </v-container>
     </v-container>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
-import { mapGetters } from 'vuex'
-import { API_ROUTES } from '@/functions'
+import { mapGetters, mapActions } from 'vuex'
+import { API_ROUTES, findNearestUpper, findNearestLower } from '@/functions'
+import Loader from '@/components/Loader.vue'
 
 
 export default {
 export default {
   name: 'ExperimentWithReference',
   name: 'ExperimentWithReference',
+  components: {
+    Loader
+  },
   props: {
   props: {
     sceneId: {
     sceneId: {
       type: String,
       type: String,
       required: true
       required: true
     }
     }
   },
   },
+  data() {
+    return {
+      referenceImage: null,
+      qualities: null,
+
+      experimentConfig: { // Experiment config sliders
+        x: 8,
+        y: 4,
+        error: null
+      },
+      extractConfig: { // Updated when `setConfig` is called
+        x: 8,
+        y: 4
+      },
+      loadingMessage: null,
+      loadingErrorMessage: null,
+      extracts: []
+    }
+  },
   computed: {
   computed: {
+    ...mapGetters(['getHostURI']),
+    isConfigNew() {
+      return this.extractConfig.x !== this.experimentConfig.x || this.extractConfig.y !== this.experimentConfig.y
+    }
   },
   },
+
   async mounted() {
   async mounted() {
-    await this.getExtracts()
+    await this.getReferenceImage()
+    await this.getQualitiesList()
+
+    // Get default extracts : min quality, cut config : x = 4, y = 4
+    await this.setConfig()
   },
   },
   methods: {
   methods: {
-    async getExtracts() {
-      const scenes = await fetch(`${this.getHostURI}${API_ROUTES.getImage()}`).then(res => res.json())
+    ...mapActions([]),
+
+    async getReferenceImage() {
+      const URI = `${this.getHostURI}${API_ROUTES.getImage(this.sceneId, 'max')}`
+      const { data } = await fetch(URI).then(res => res.json())
+      this.referenceImage = this.getHostURI + data.link
+    },
+
+    async getQualitiesList() {
+      const URI = `${this.getHostURI}${API_ROUTES.listSceneQualities(this.sceneId)}`
+      const { data } = await fetch(URI).then(res => res.json())
+      this.qualities = data
+    },
+
+    async getExtracts(quality = 'max') {
+      const URI = `${this.getHostURI}${API_ROUTES.getImageExtracts(this.sceneId, quality, this.extractConfig.x, this.extractConfig.y)}`
+      const { data } = await fetch(URI)
+        .then(async res => {
+          res.json = await res.json()
+          return res
+        })
+        .then(res => {
+          if (!res.ok) throw new Error(res.json.message + res.json.data ? `\n${res.json.data}` : '')
+          return res.json
+        })
+      return data
+    },
+
+    async setConfig() {
+      // Check if the config is the same
+      if (this.extracts.length > 0 && !this.isConfigNew) return
+
+      this.loadingMessage = 'Loading configuration extracts...'
+      this.loadingErrorMessage = null
+      try {
+        this.extractConfig.x = this.experimentConfig.x
+        this.extractConfig.y = this.experimentConfig.y
+        const data = await this.getExtracts()
+        const hostURI = this.getHostURI
+        this.extracts = data.extracts.map((url, i) => ({
+          link: hostURI + url,
+          quality: data.info.image.quality,
+          zone: i + 1,
+          index: i,
+          nextQuality: findNearestUpper(data.info.image.quality, this.qualities),
+          precQuality: findNearestLower(data.info.image.quality, this.qualities),
+          loading: false
+        }))
+      }
+      catch (err) {
+        console.error('Failed to load new configuration', err)
+        this.loadingErrorMessage = 'Failed to load new configuration. ' + err.message
+      }
+      this.loadingMessage = null
+    },
+
+    async extractAction(event, extractObj) {
+      console.log(event, extractObj)
+      const { index, nextQuality, precQuality, quality } = extractObj
+
+      let newQuality
+      if (event.button === 0) newQuality = precQuality // Left click
+      if (event.button === 2) newQuality = nextQuality // Right click
+
+      // Do not load a new extract if same quality
+      if (newQuality === quality) return
+
+      // Set loading state
+      this.extracts[index].loading = true
+      try {
+        // Loading new extract
+        const data = await this.getExtracts(newQuality)
+        this.extracts[index].link = this.getHostURI + data.extracts[index]
+        this.extracts[index].quality = data.info.image.quality
+        this.extracts[index].nextQuality = findNearestUpper(data.info.image.quality, this.qualities)
+        this.extracts[index].precQuality = findNearestLower(data.info.image.quality, this.qualities)
+        this.extracts[index].loading = false
+      }
+      catch (err) {
+        // TODO: toast message if fail
+        console.error('Failed to load extract', err)
+      }
+      finally {
+        this.extracts[index].loading = false
+      }
     }
     }
   }
   }
 }
 }
 </script>
 </script>
+
+<style scoped>
+.height100 {
+  height: 100%;
+}
+.img-loader {
+  height: 100%;
+  width: 0px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.cursor {
+  cursor: pointer;
+}
+.extract:hover {
+  z-index: 999999;
+  outline: 2px #f4f4f4 solid;
+}
+</style>

+ 17 - 5
test/api/getImage.js

@@ -94,11 +94,23 @@ test('GET /getImage?sceneName=bathroom&imageQuality=10', async t => {
   t.is(res.status, 200, json(res))
   t.is(res.status, 200, json(res))
   t.is(res.body.data.link, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
   t.is(res.body.data.link, `${imageServedUrl}/bathroom/bathroom_00010.png`, json(res.body))
   t.deepEqual(res.body.data, {
   t.deepEqual(res.body.data, {
-    'link': `${imageServedUrl}/bathroom/bathroom_00010.png`,
-    'fileName': 'bathroom_00010.png',
-    'sceneName': 'bathroom',
-    'quality': 10,
-    'ext': 'png'
+    link: `${imageServedUrl}/bathroom/bathroom_00010.png`,
+    fileName: 'bathroom_00010.png',
+    sceneName: 'bathroom',
+    quality: 10,
+    ext: 'png',
+    metadata: {
+      format: 'png',
+      width: 800,
+      height: 800,
+      space: 'srgb',
+      channels: 3,
+      depth: 'uchar',
+      density: 72,
+      isProgressive: false,
+      hasProfile: false,
+      hasAlpha: false
+    }
   }, json(res.body))
   }, json(res.body))
 
 
   // Check link is accessible and is an image
   // Check link is accessible and is an image

+ 27 - 5
test/api/getImageExtracts.js

@@ -115,11 +115,33 @@ test.serial('GET /getImageExtracts?sceneName=bathroom&imageQuality=10&horizontal
   t.true(Array.isArray(res.body.data.extracts), json(res.body))
   t.true(Array.isArray(res.body.data.extracts), json(res.body))
   t.is(res.body.data.extracts[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
   t.is(res.body.data.extracts[0], `${imageServedUrl}/bathroom/extracts/x5_y2/zone00001/bathroom_zone00001_10.png`, json(res.body))
   t.deepEqual(res.body.data.info, {
   t.deepEqual(res.body.data.info, {
-    link: `${imageServedUrl}/bathroom/bathroom_00010.png`,
-    fileName: 'bathroom_00010.png',
-    sceneName: 'bathroom',
-    quality: 10,
-    ext: 'png'
+    extractsConfig: {
+      x: 5,
+      y: 2
+    },
+    extractsSize: {
+      width: 160,
+      height: 400
+    },
+    image: {
+      link: `${imageServedUrl}/bathroom/bathroom_00010.png`,
+      fileName: 'bathroom_00010.png',
+      sceneName: 'bathroom',
+      quality: 10,
+      ext: 'png',
+      metadata: {
+        format: 'png',
+        width: 800,
+        height: 800,
+        space: 'srgb',
+        channels: 3,
+        depth: 'uchar',
+        density: 72,
+        isProgressive: false,
+        hasProfile: false,
+        hasAlpha: false
+      }
+    }
   }, json(res.body))
   }, json(res.body))
 
 
   // Check link is accessible and is an image
   // Check link is accessible and is an image