Area delete zone crop

This commit is contained in:
Theophyl Hoguet committed 2023-04-13 20:55:23 +02:00
1 parent db2269f26c
commit e101341549
10 files changed
+199 -53

No files matched your search

@@ -3,23 +3,21 @@ package pdl.backend.Algorithm.Parameters;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import pdl.backend.Area;
public class AreaParameter extends Parameter { public class AreaParameter extends Parameter {
private final Area area;
public AreaParameter(String name, String path) { public AreaParameter(String name, String path) {
super(name, path, "area"); super(name, path, "area");
area = new Area(0, 0, 0, 0);
} }
@Override @Override
public void setValue(String value) { public void setValue(String value) {
String[] values = value.split(";"); this.area.setValues(value);
if (values.length != 4) { super.setValueObject(this.area);
throw new IllegalArgumentException("Area parameter must have 4 values");
}
int[] intValues = new int[4];
for (int i = 0; i < 4; i++) {
intValues[i] = Integer.parseInt(values[i]);
}
super.setValueObject(intValues);
} }
public ObjectNode getNode(ObjectMapper mapper) { public ObjectNode getNode(ObjectMapper mapper) {
@@ -0,0 +1,70 @@
package pdl.backend;
public class Area {
private int xMin;
private int yMin;
private int xMax;
private int yMax;
public Area(int xMin, int yMin, int xMax, int yMax) {
this.xMin = xMin;
this.yMin = yMin;
this.xMax = xMax;
this.yMax = yMax;
}
public Area(String str) {
this.setValues(str);
}
public void setValues(String str) {
String[] values = str.split(";");
if (values.length != 4) {
throw new IllegalArgumentException("Area parameter must have 4 values");
}
int[] intValues = new int[4];
for (int i = 0; i < 4; i++) {
intValues[i] = Integer.parseInt(values[i]);
}
this.xMin = intValues[0];
this.yMin = intValues[1];
this.xMax = intValues[2];
this.yMax = intValues[3];
}
public int getxMin() {
return xMin;
}
public void setxMin(int xMin) {
this.xMin = xMin;
}
public int getyMin() {
return yMin;
}
public void setyMin(int yMin) {
this.yMin = yMin;
}
public int getxMax() {
return xMax;
}
public void setxMax(int xMax) {
this.xMax = xMax;
}
public int getyMax() {
return yMax;
}
public void setyMax(int yMax) {
this.yMax = yMax;
}
public boolean isEmpty() {
return xMin == 0 && yMin == 0 && xMax == 0 && yMax == 0;
}
}
@@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
import boofcv.struct.border.BorderType; import boofcv.struct.border.BorderType;
import boofcv.struct.image.GrayU8; import boofcv.struct.image.GrayU8;
import boofcv.struct.image.Planar; import boofcv.struct.image.Planar;
import pdl.backend.Area;
import pdl.backend.Algorithm.Algorithm; import pdl.backend.Algorithm.Algorithm;
import pdl.backend.Algorithm.Parameters.AreaParameter; import pdl.backend.Algorithm.Parameters.AreaParameter;
import pdl.backend.Algorithm.Parameters.DoubleParameter; import pdl.backend.Algorithm.Parameters.DoubleParameter;
@@ -30,7 +31,9 @@ public class AlgorithmController {
@Autowired @Autowired
private ObjectMapper mapper; private ObjectMapper mapper;
public static List<Algorithm> algorithmList() { public final static List<Algorithm> ALGORITHMS = algorithmList();
private final static List<Algorithm> algorithmList() {
final String[] borderTypes = { "SKIP", "ZERO", "NORMALIZED", "REFLECT", "EXTENDED", "WRAP" }; final String[] borderTypes = { "SKIP", "ZERO", "NORMALIZED", "REFLECT", "EXTENDED", "WRAP" };
final String[] fillingTypes = { "SKIP", "CONVOLUTION", "LEFT", "RIGHT", "TOP", "BOTTOM" }; final String[] fillingTypes = { "SKIP", "CONVOLUTION", "LEFT", "RIGHT", "TOP", "BOTTOM" };
ArrayList<Algorithm> algorithms = new ArrayList<Algorithm>(); ArrayList<Algorithm> algorithms = new ArrayList<Algorithm>();
@@ -81,16 +84,22 @@ public class AlgorithmController {
ImageProcessing.sepiaFilter(input); ImageProcessing.sepiaFilter(input);
})); }));
algorithms.add(new Algorithm("Suppression zone", "deleteArea", algorithms.add(new Algorithm("Suppression zone", "deleteArea",
Arrays.asList(new AreaParameter("area", "Zone"), Arrays.asList(new AreaParameter("Zone", "area"),
new SelectParameter("fillingType", "Type de remplissage", fillingTypes)), new SelectParameter("fillingType", "Type de remplissage", fillingTypes)),
(Planar<GrayU8> input, List<Object> para) -> { (Planar<GrayU8> input, List<Object> para) -> {
ImageProcessing.deleteArea(input, (int[]) para.get(0), FillingType.valueOf((String) para.get(1))); ImageProcessing.deleteArea(input, (Area) para.get(0),
FillingType.valueOf((String) para.get(1)));
})); }));
algorithms.add(new Algorithm("Filtre de bruit", "noise", algorithms.add(new Algorithm("Filtre de bruit", "noise",
Arrays.asList(new IntegerParameter("intensity", "Intensité", 1, 100, 1)), Arrays.asList(new IntegerParameter("intensity", "Intensité", 1, 100, 1)),
(Planar<GrayU8> input, List<Object> para) -> { (Planar<GrayU8> input, List<Object> para) -> {
ImageProcessing.gaussianNoiseFilter(input, (int) para.get(0)); ImageProcessing.gaussianNoiseFilter(input, (int) para.get(0));
})); }));
algorithms.add(new Algorithm("Rognage", "crop",
Arrays.asList(new AreaParameter("Zone", "area")),
(Planar<GrayU8> input, List<Object> para) -> {
ImageProcessing.crop(input, (Area) para.get(0));
}));
return algorithms; return algorithms;
} }
@@ -182,7 +182,7 @@ public class ImageController {
return ResponseEntity.ok().contentType(imageToFilter.getMediaType()) return ResponseEntity.ok().contentType(imageToFilter.getMediaType())
.body(new InputStreamResource(inputStream)); .body(new InputStreamResource(inputStream));
} }
final List<Algorithm> algos = AlgorithmController.algorithmList(); final List<Algorithm> algos = AlgorithmController.ALGORITHMS;
// Vérifie la présence du paramètre "algorithm" // Vérifie la présence du paramètre "algorithm"
if (!parameters.containsKey("algorithm")) { if (!parameters.containsKey("algorithm")) {
return JSONError("First parameter should be 'algorithm'", HttpStatus.BAD_REQUEST); return JSONError("First parameter should be 'algorithm'", HttpStatus.BAD_REQUEST);
@@ -211,7 +211,6 @@ public class ImageController {
} }
try { try {
a.apply(input); a.apply(input);
} catch (Exception e) { } catch (Exception e) {
return JSONError("There was an internal server error", HttpStatus.INTERNAL_SERVER_ERROR); return JSONError("There was an internal server error", HttpStatus.INTERNAL_SERVER_ERROR);
} }
@@ -3,6 +3,7 @@ package pdl.backend.Image;
import boofcv.struct.border.BorderType; import boofcv.struct.border.BorderType;
import boofcv.struct.image.GrayU8; import boofcv.struct.image.GrayU8;
import boofcv.struct.image.Planar; import boofcv.struct.image.Planar;
import pdl.backend.Area;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -673,14 +674,14 @@ public final class ImageProcessing {
* @param area Les coordonnées de la zone à supprimer * @param area Les coordonnées de la zone à supprimer
* @param filling Le type de remplissage * @param filling Le type de remplissage
*/ */
public static void deleteArea(Planar<GrayU8> input, int[] area, FillingType filling) { public static void deleteArea(Planar<GrayU8> input, Area area, FillingType filling) {
if (area.length != 4) { if (area.isEmpty()) {
throw new IllegalArgumentException("Les coordonnées sont incorrectes"); throw new IllegalArgumentException("La zone est vide");
} }
int xMin = area[0]; int xMin = area.getxMin();
int xMax = area[1]; int xMax = area.getxMax();
int yMin = area[2]; int yMin = area.getyMin();
int yMax = area[3]; int yMax = area.getyMax();
if (xMin < 0 || xMax > input.width || yMin < 0 || yMax > input.height) { if (xMin < 0 || xMax > input.width || yMin < 0 || yMax > input.height) {
throw new IllegalArgumentException("Les coordonnées sont en dehors de l'image"); throw new IllegalArgumentException("Les coordonnées sont en dehors de l'image");
} }
@@ -796,4 +797,29 @@ public final class ImageProcessing {
}); });
counter.set(-1); counter.set(-1);
} }
/**
* Rogne l'image en fonction de l'area donnée en paramètre
*
* @param input L'image à rogner
* @param area L'area à utiliser pour rogner l'image
*/
public static void crop(Planar<GrayU8> input, Area area) {
if (area.isEmpty())
throw new IllegalArgumentException("La zone est vide");
int xMin = area.getxMin();
int xMax = area.getxMax();
int yMin = area.getyMin();
int yMax = area.getyMax();
if (xMin < 0 || xMax > input.width || yMin < 0 || yMax > input.height) {
throw new IllegalArgumentException("Les coordonnées sont en dehors de l'image");
}
Planar<GrayU8> output = new Planar<>(GrayU8.class, xMax - xMin + 1, yMax - yMin + 1, input.getNumBands());
for (int i = xMin; i <= xMax; i++) {
for (int j = yMin; j <= yMax; j++) {
setRGBValue(output, i - xMin, j - yMin, getRGBValue(input, i, j));
}
}
input.setTo(output);
}
} }
+16 -5
View File
@@ -4,7 +4,7 @@ import HomeSelect from '@/components/HomeSelect.vue'
import HomeEditor from '@/components/HomeEditor.vue' import HomeEditor from '@/components/HomeEditor.vue'
import { defineComponent } from 'vue' import { defineComponent } from 'vue'
import { ImageType } from '@/image' import { ImageType } from '@/image'
import { Filter } from '@/filter' import { Area, Filter, FilterType } from '@/filter'
import { api } from '@/http-api' import { api } from '@/http-api'
</script> </script>
<script lang="ts"> <script lang="ts">
@@ -32,7 +32,7 @@ export default defineComponent({
imageSelectedId: -1, imageSelectedId: -1,
editor: false, editor: false,
newId: -1, newId: -1,
selectedArea: { xmin: 0, ymin: 0, xmax: 0, ymax: 0 }, selectedArea: new Area(0, 0, 0, 0),
processed: false, processed: false,
progress: 0 progress: 0
} }
@@ -52,7 +52,7 @@ export default defineComponent({
deep: true deep: true
}, },
editor() { editor() {
this.selectedArea = { xmin: 0, ymin: 0, xmax: 0, ymax: 0 }; this.selectedArea = new Area(0, 0, 0, 0);
} }
}, },
methods: { methods: {
@@ -69,11 +69,18 @@ export default defineComponent({
this.imageSelectedId = this.images.keys().next().value; this.imageSelectedId = this.images.keys().next().value;
}, },
async applyFilter(filter: Filter) { async applyFilter(filter: Filter) {
console.log("applyfilter")
if (filter === undefined || filter === null) { if (filter === undefined || filter === null) {
return; return;
} }
console.log(filter); // if filter contain AreaParameter, we need to set the area
const newImage = api.applyAlgorithm(this.imageSelectedId, filter, this.selectedArea); for (const arg of filter.parameters) {
console.log("applyfilter")
if (arg.type === FilterType.area) {
arg.value = this.selectedArea;
}
}
const newImage = api.applyAlgorithm(this.imageSelectedId, filter);
newImage.then((id) => { newImage.then((id) => {
this.processed = !this.processed; this.processed = !this.processed;
this.newId = parseInt(id); this.newId = parseInt(id);
@@ -81,6 +88,10 @@ export default defineComponent({
clearInterval(timer); clearInterval(timer);
this.progress = 0; this.progress = 0;
}); });
newImage.catch((err) => {
console.log(err);
clearInterval(timer);
});
// set a timer to get the progress of the filter // set a timer to get the progress of the filter
const timer = setInterval(async () => { const timer = setInterval(async () => {
const progress = (await api.algorithmsProgress()).progress; const progress = (await api.algorithmsProgress()).progress;
+6 -3
View File
@@ -24,7 +24,7 @@ export default defineComponent({
progress: { progress: {
type: Number, type: Number,
required: true, required: true,
} },
}, },
emits: { emits: {
deleteImage() { deleteImage() {
@@ -53,8 +53,8 @@ export default defineComponent({
return; return;
const filter = this.filters[this.filterSelectId]; const filter = this.filters[this.filterSelectId];
for (const arg of filter.parameters) { for (const arg of filter.parameters) {
if (arg.value === undefined) { if (arg.type !== FilterType.area && arg.value === undefined) {
this.error = ""; this.error = "Un paramètre n'a pas été renseigné";
return; return;
} }
} }
@@ -147,6 +147,9 @@ export default defineComponent({
<label>{{ parameter.name }}: </label> <label>{{ parameter.name }}: </label>
<input type="color" v-model="parameter.value" /> <input type="color" v-model="parameter.value" />
</div> </div>
<div v-else-if="parameter.type === FilterType.area">
<label> Veuillez selectioner une {{ parameter.name }}</label>
</div>
</div> </div>
<button :style="{ background: styledProgress() }" @mouseenter="error = areInputValid()" <button :style="{ background: styledProgress() }" @mouseenter="error = areInputValid()"
@mouseleave="error = areInputValid()" class="button" :class="{ errorClass: error !== '' }" @mouseleave="error = areInputValid()" class="button" :class="{ errorClass: error !== '' }"
+19 -18
View File
@@ -2,6 +2,7 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { api } from '@/http-api'; import { api } from '@/http-api';
import { ImageType } from '@/image'; import { ImageType } from '@/image';
import { Area } from '@/filter';
</script> </script>
<script lang="ts"> <script lang="ts">
export default defineComponent({ export default defineComponent({
@@ -18,7 +19,7 @@ export default defineComponent({
type: String type: String
}, },
selectedArea: { selectedArea: {
type: Object as () => { xmin: number, ymin: number, xmax: number, ymax: number }, type: Area,
} }
}, },
data() { data() {
@@ -60,7 +61,7 @@ export default defineComponent({
ctx.drawImage(this.image, 0, 0, this.image.naturalWidth, this.image.naturalHeight); ctx.drawImage(this.image, 0, 0, this.image.naturalWidth, this.image.naturalHeight);
if (this.selectedArea === undefined) return; if (this.selectedArea === undefined) return;
ctx.fillStyle = "rgba(0, 0, 50, 0.5)"; ctx.fillStyle = "rgba(0, 0, 50, 0.5)";
ctx.fillRect(this.selectedArea.xmin, this.selectedArea.ymin, this.selectedArea.xmax - this.selectedArea.xmin, this.selectedArea.ymax - this.selectedArea.ymin); ctx.fillRect(this.selectedArea.xMin, this.selectedArea.yMin, this.selectedArea.xMax - this.selectedArea.xMin, this.selectedArea.yMax - this.selectedArea.yMin);
} }
}, },
handleMouse(event: MouseEvent) { handleMouse(event: MouseEvent) {
@@ -87,31 +88,31 @@ export default defineComponent({
const x = event.clientX - rect.left; const x = event.clientX - rect.left;
const y = event.clientY - rect.top; const y = event.clientY - rect.top;
if (event.type === "mousedown") { if (event.type === "mousedown") {
this.selectedArea.xmin = x * ratio; this.selectedArea.xMin = x * ratio;
this.selectedArea.ymin = y * ratio; this.selectedArea.yMin = y * ratio;
this.selectedArea.xmax = x * ratio; this.selectedArea.xMax = x * ratio;
this.selectedArea.ymax = y * ratio; this.selectedArea.yMax = y * ratio;
this.mouseDowned = true; this.mouseDowned = true;
this.drawImage(); this.drawImage();
} }
else if (this.mouseDowned && event.type === "mousemove") { else if (this.mouseDowned && event.type === "mousemove") {
this.selectedArea.xmax = x * ratio; this.selectedArea.xMax = x * ratio;
this.selectedArea.ymax = y * ratio; this.selectedArea.yMax = y * ratio;
this.drawImage(); this.drawImage();
} }
else if (event.type === "mouseup") { else if (event.type === "mouseup") {
this.mouseDowned = false; this.mouseDowned = false;
this.selectedArea.xmax = x * ratio; this.selectedArea.xMax = x * ratio;
this.selectedArea.ymax = y * ratio; this.selectedArea.yMax = y * ratio;
if (this.selectedArea.xmin > this.selectedArea.xmax) { if (this.selectedArea.xMin > this.selectedArea.xMax) {
const tmp = this.selectedArea.xmin; const tmp = this.selectedArea.xMin;
this.selectedArea.xmin = this.selectedArea.xmax; this.selectedArea.xMin = this.selectedArea.xMax;
this.selectedArea.xmax = tmp; this.selectedArea.xMax = tmp;
} }
if (this.selectedArea.ymin > this.selectedArea.ymax) { if (this.selectedArea.yMin > this.selectedArea.yMax) {
const tmp = this.selectedArea.ymin; const tmp = this.selectedArea.yMin;
this.selectedArea.ymin = this.selectedArea.ymax; this.selectedArea.yMin = this.selectedArea.yMax;
this.selectedArea.ymax = tmp; this.selectedArea.yMax = tmp;
} }
this.drawImage(); this.drawImage();
} }
+34 -1
View File
@@ -2,7 +2,8 @@ export enum FilterType {
"range" = "range", "range" = "range",
"select" = "select", "select" = "select",
"color" = "color", "color" = "color",
"boolean" = "boolean" "boolean" = "boolean",
"area" = "area"
} }
@@ -52,6 +53,31 @@ export class SelectParameters extends Parameters {
} }
} }
export class Area {
xMin: number
xMax: number
yMin: number
yMax: number
constructor(xMin: number, xMax: number, yMin: number, yMax: number) {
this.xMin = xMin
this.xMax = xMax
this.yMin = yMin
this.yMax = yMax
}
toString(): string {
return Math.round(this.xMin) + ";" + Math.round(this.yMin) + ";" + Math.round(this.xMax) + ";" + Math.round(this.yMax)
}
}
export class AreaParameters extends Parameters {
value: Area
constructor(name: string, displayName: string, value: Area) {
super(name, displayName, FilterType.select)
this.value = value;
}
}
export class Filter { export class Filter {
name: string name: string
@@ -75,6 +101,13 @@ export function getParameters(f: Filter): string {
case FilterType.select: case FilterType.select:
const paramCastSelect = param as SelectParameters const paramCastSelect = param as SelectParameters
return paramCastSelect.name + "=" + paramCastSelect.value return paramCastSelect.name + "=" + paramCastSelect.value
case FilterType.color:
const paramCastColor = param as ColorParameters
return paramCastColor.name + "=" + paramCastColor.value
case FilterType.area:
console.log("area")
const paramCastArea = param as AreaParameters
return paramCastArea.name + "=" + paramCastArea.value.toString()
} }
} }
).join("&") ).join("&")
+1 -5
View File
@@ -62,15 +62,11 @@ const stableDiffRequests = {
post: (url: string, body: {}) => stableDiffInstance.post(url, body, { headers: { "Content-Type": "application/json" }, }).then(responseBody), post: (url: string, body: {}) => stableDiffInstance.post(url, body, { headers: { "Content-Type": "application/json" }, }).then(responseBody),
}; };
function areaToString(area: area): string {
return `&xmin=${Math.round(area.xmin)}&ymin=${Math.round(area.ymin)}&xmax=${Math.round(area.xmax)}&ymax=${Math.round(area.ymax)}`;
}
export const api = { export const api = {
getImageList: (): Promise<ImageType[]> => requests.get('images', {}), getImageList: (): Promise<ImageType[]> => requests.get('images', {}),
getAlgorithmList: (): Promise<Filter[]> => requests.get('algorithms', {}), getAlgorithmList: (): Promise<Filter[]> => requests.get('algorithms', {}),
getImage: (id: number): Promise<Blob> => requests.get(`images/${id}`, { responseType: "blob" }), getImage: (id: number): Promise<Blob> => requests.get(`images/${id}`, { responseType: "blob" }),
applyAlgorithm: (id: number, filter: Filter, selectedArea: area): Promise<string> => requests.get(`images/${id}?algorithm=${filter.path + getParameters(filter) + areaToString(selectedArea)}`, {}), applyAlgorithm: (id: number, filter: Filter): Promise<string> => requests.get(`images/${id}?algorithm=${filter.path + getParameters(filter)}`, {}),
algorithmsProgress: (): Promise<algorithmProgressInterface> => requests.get('images/progress', {}), algorithmsProgress: (): Promise<algorithmProgressInterface> => requests.get('images/progress', {}),
createImage: (form: FormData): Promise<HttpStatusCode> => requests.post('images', form), createImage: (form: FormData): Promise<HttpStatusCode> => requests.post('images', form),
deleteImage: (id: number): Promise<void> => requests.delete(`images/${id}`), deleteImage: (id: number): Promise<void> => requests.delete(`images/${id}`),