Area delete zone crop
This commit is contained in:
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.node.ObjectNode;
|
||||
|
||||
import pdl.backend.Area;
|
||||
|
||||
public class AreaParameter extends Parameter {
|
||||
|
||||
private final Area area;
|
||||
|
||||
public AreaParameter(String name, String path) {
|
||||
super(name, path, "area");
|
||||
area = new Area(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
String[] values = value.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]);
|
||||
}
|
||||
super.setValueObject(intValues);
|
||||
this.area.setValues(value);
|
||||
super.setValueObject(this.area);
|
||||
}
|
||||
|
||||
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.image.GrayU8;
|
||||
import boofcv.struct.image.Planar;
|
||||
import pdl.backend.Area;
|
||||
import pdl.backend.Algorithm.Algorithm;
|
||||
import pdl.backend.Algorithm.Parameters.AreaParameter;
|
||||
import pdl.backend.Algorithm.Parameters.DoubleParameter;
|
||||
@@ -30,7 +31,9 @@ public class AlgorithmController {
|
||||
@Autowired
|
||||
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[] fillingTypes = { "SKIP", "CONVOLUTION", "LEFT", "RIGHT", "TOP", "BOTTOM" };
|
||||
ArrayList<Algorithm> algorithms = new ArrayList<Algorithm>();
|
||||
@@ -81,16 +84,22 @@ public class AlgorithmController {
|
||||
ImageProcessing.sepiaFilter(input);
|
||||
}));
|
||||
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)),
|
||||
(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",
|
||||
Arrays.asList(new IntegerParameter("intensity", "Intensité", 1, 100, 1)),
|
||||
(Planar<GrayU8> input, List<Object> para) -> {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ public class ImageController {
|
||||
return ResponseEntity.ok().contentType(imageToFilter.getMediaType())
|
||||
.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"
|
||||
if (!parameters.containsKey("algorithm")) {
|
||||
return JSONError("First parameter should be 'algorithm'", HttpStatus.BAD_REQUEST);
|
||||
@@ -211,7 +211,6 @@ public class ImageController {
|
||||
}
|
||||
try {
|
||||
a.apply(input);
|
||||
|
||||
} catch (Exception e) {
|
||||
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.image.GrayU8;
|
||||
import boofcv.struct.image.Planar;
|
||||
import pdl.backend.Area;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -673,14 +674,14 @@ public final class ImageProcessing {
|
||||
* @param area Les coordonnées de la zone à supprimer
|
||||
* @param filling Le type de remplissage
|
||||
*/
|
||||
public static void deleteArea(Planar<GrayU8> input, int[] area, FillingType filling) {
|
||||
if (area.length != 4) {
|
||||
throw new IllegalArgumentException("Les coordonnées sont incorrectes");
|
||||
public static void deleteArea(Planar<GrayU8> input, Area area, FillingType filling) {
|
||||
if (area.isEmpty()) {
|
||||
throw new IllegalArgumentException("La zone est vide");
|
||||
}
|
||||
int xMin = area[0];
|
||||
int xMax = area[1];
|
||||
int yMin = area[2];
|
||||
int yMax = area[3];
|
||||
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");
|
||||
}
|
||||
@@ -796,4 +797,29 @@ public final class ImageProcessing {
|
||||
});
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import HomeSelect from '@/components/HomeSelect.vue'
|
||||
import HomeEditor from '@/components/HomeEditor.vue'
|
||||
import { defineComponent } from 'vue'
|
||||
import { ImageType } from '@/image'
|
||||
import { Filter } from '@/filter'
|
||||
import { Area, Filter, FilterType } from '@/filter'
|
||||
import { api } from '@/http-api'
|
||||
</script>
|
||||
<script lang="ts">
|
||||
@@ -32,7 +32,7 @@ export default defineComponent({
|
||||
imageSelectedId: -1,
|
||||
editor: false,
|
||||
newId: -1,
|
||||
selectedArea: { xmin: 0, ymin: 0, xmax: 0, ymax: 0 },
|
||||
selectedArea: new Area(0, 0, 0, 0),
|
||||
processed: false,
|
||||
progress: 0
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export default defineComponent({
|
||||
deep: true
|
||||
},
|
||||
editor() {
|
||||
this.selectedArea = { xmin: 0, ymin: 0, xmax: 0, ymax: 0 };
|
||||
this.selectedArea = new Area(0, 0, 0, 0);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -69,11 +69,18 @@ export default defineComponent({
|
||||
this.imageSelectedId = this.images.keys().next().value;
|
||||
},
|
||||
async applyFilter(filter: Filter) {
|
||||
console.log("applyfilter")
|
||||
if (filter === undefined || filter === null) {
|
||||
return;
|
||||
}
|
||||
console.log(filter);
|
||||
const newImage = api.applyAlgorithm(this.imageSelectedId, filter, this.selectedArea);
|
||||
// if filter contain AreaParameter, we need to set the area
|
||||
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) => {
|
||||
this.processed = !this.processed;
|
||||
this.newId = parseInt(id);
|
||||
@@ -81,6 +88,10 @@ export default defineComponent({
|
||||
clearInterval(timer);
|
||||
this.progress = 0;
|
||||
});
|
||||
newImage.catch((err) => {
|
||||
console.log(err);
|
||||
clearInterval(timer);
|
||||
});
|
||||
// set a timer to get the progress of the filter
|
||||
const timer = setInterval(async () => {
|
||||
const progress = (await api.algorithmsProgress()).progress;
|
||||
|
||||
@@ -24,7 +24,7 @@ export default defineComponent({
|
||||
progress: {
|
||||
type: Number,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
deleteImage() {
|
||||
@@ -53,8 +53,8 @@ export default defineComponent({
|
||||
return;
|
||||
const filter = this.filters[this.filterSelectId];
|
||||
for (const arg of filter.parameters) {
|
||||
if (arg.value === undefined) {
|
||||
this.error = "";
|
||||
if (arg.type !== FilterType.area && arg.value === undefined) {
|
||||
this.error = "Un paramètre n'a pas été renseigné";
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -147,6 +147,9 @@ export default defineComponent({
|
||||
<label>{{ parameter.name }}: </label>
|
||||
<input type="color" v-model="parameter.value" />
|
||||
</div>
|
||||
<div v-else-if="parameter.type === FilterType.area">
|
||||
<label> Veuillez selectioner une {{ parameter.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<button :style="{ background: styledProgress() }" @mouseenter="error = areInputValid()"
|
||||
@mouseleave="error = areInputValid()" class="button" :class="{ errorClass: error !== '' }"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { api } from '@/http-api';
|
||||
import { ImageType } from '@/image';
|
||||
import { Area } from '@/filter';
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
@@ -18,7 +19,7 @@ export default defineComponent({
|
||||
type: String
|
||||
},
|
||||
selectedArea: {
|
||||
type: Object as () => { xmin: number, ymin: number, xmax: number, ymax: number },
|
||||
type: Area,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -60,7 +61,7 @@ export default defineComponent({
|
||||
ctx.drawImage(this.image, 0, 0, this.image.naturalWidth, this.image.naturalHeight);
|
||||
if (this.selectedArea === undefined) return;
|
||||
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) {
|
||||
@@ -87,31 +88,31 @@ export default defineComponent({
|
||||
const x = event.clientX - rect.left;
|
||||
const y = event.clientY - rect.top;
|
||||
if (event.type === "mousedown") {
|
||||
this.selectedArea.xmin = x * ratio;
|
||||
this.selectedArea.ymin = y * ratio;
|
||||
this.selectedArea.xmax = x * ratio;
|
||||
this.selectedArea.ymax = y * ratio;
|
||||
this.selectedArea.xMin = x * ratio;
|
||||
this.selectedArea.yMin = y * ratio;
|
||||
this.selectedArea.xMax = x * ratio;
|
||||
this.selectedArea.yMax = y * ratio;
|
||||
this.mouseDowned = true;
|
||||
this.drawImage();
|
||||
}
|
||||
else if (this.mouseDowned && event.type === "mousemove") {
|
||||
this.selectedArea.xmax = x * ratio;
|
||||
this.selectedArea.ymax = y * ratio;
|
||||
this.selectedArea.xMax = x * ratio;
|
||||
this.selectedArea.yMax = y * ratio;
|
||||
this.drawImage();
|
||||
}
|
||||
else if (event.type === "mouseup") {
|
||||
this.mouseDowned = false;
|
||||
this.selectedArea.xmax = x * ratio;
|
||||
this.selectedArea.ymax = y * ratio;
|
||||
if (this.selectedArea.xmin > this.selectedArea.xmax) {
|
||||
const tmp = this.selectedArea.xmin;
|
||||
this.selectedArea.xmin = this.selectedArea.xmax;
|
||||
this.selectedArea.xmax = tmp;
|
||||
this.selectedArea.xMax = x * ratio;
|
||||
this.selectedArea.yMax = y * ratio;
|
||||
if (this.selectedArea.xMin > this.selectedArea.xMax) {
|
||||
const tmp = this.selectedArea.xMin;
|
||||
this.selectedArea.xMin = this.selectedArea.xMax;
|
||||
this.selectedArea.xMax = tmp;
|
||||
}
|
||||
if (this.selectedArea.ymin > this.selectedArea.ymax) {
|
||||
const tmp = this.selectedArea.ymin;
|
||||
this.selectedArea.ymin = this.selectedArea.ymax;
|
||||
this.selectedArea.ymax = tmp;
|
||||
if (this.selectedArea.yMin > this.selectedArea.yMax) {
|
||||
const tmp = this.selectedArea.yMin;
|
||||
this.selectedArea.yMin = this.selectedArea.yMax;
|
||||
this.selectedArea.yMax = tmp;
|
||||
}
|
||||
this.drawImage();
|
||||
}
|
||||
|
||||
+34
-1
@@ -2,7 +2,8 @@ export enum FilterType {
|
||||
"range" = "range",
|
||||
"select" = "select",
|
||||
"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 {
|
||||
name: string
|
||||
@@ -75,6 +101,13 @@ export function getParameters(f: Filter): string {
|
||||
case FilterType.select:
|
||||
const paramCastSelect = param as SelectParameters
|
||||
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("&")
|
||||
|
||||
@@ -62,15 +62,11 @@ const stableDiffRequests = {
|
||||
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 = {
|
||||
getImageList: (): Promise<ImageType[]> => requests.get('images', {}),
|
||||
getAlgorithmList: (): Promise<Filter[]> => requests.get('algorithms', {}),
|
||||
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', {}),
|
||||
createImage: (form: FormData): Promise<HttpStatusCode> => requests.post('images', form),
|
||||
deleteImage: (id: number): Promise<void> => requests.delete(`images/${id}`),
|
||||
|
||||
Reference in new issue
Block a user