Merge branch 'main' into deleteZone
This commit is contained in:
commit
661524d675
31 files changed
+1111
-477
No files matched your search
@@ -1,56 +0,0 @@
|
|||||||
package pdl.backend;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
|
||||||
|
|
||||||
public class Algorithm {
|
|
||||||
private final String name;
|
|
||||||
private final String path;
|
|
||||||
private final ArrayList<Parameter> parameters;
|
|
||||||
|
|
||||||
public Algorithm(String name, String path) {
|
|
||||||
this.name = name;
|
|
||||||
this.path = path;
|
|
||||||
this.parameters = new ArrayList<>();
|
|
||||||
switch (path) {
|
|
||||||
case "changeLuminosity":
|
|
||||||
Parameter param1 = new Parameter("delta", "delta", -255, 255, 1);
|
|
||||||
this.parameters.add(param1);
|
|
||||||
break;
|
|
||||||
case "histogram":
|
|
||||||
break;
|
|
||||||
case "colorFilter":
|
|
||||||
Parameter param2 = new Parameter("hue", "Teinte", 0, 359, 1);
|
|
||||||
this.parameters.add(param2);
|
|
||||||
break;
|
|
||||||
case "meanFilter":
|
|
||||||
Parameter param3 = new Parameter("size", "Taille du filtre", 1, 101, 2);
|
|
||||||
this.parameters.add(param3);
|
|
||||||
break;
|
|
||||||
case "gaussienFilter":
|
|
||||||
// TODO
|
|
||||||
// Parameter param4 = new Parameter("size", "Taille du filtre", 1, 101, 2);
|
|
||||||
// this.parameters.add(param4);
|
|
||||||
break;
|
|
||||||
case "contours":
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid path");
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPath() {
|
|
||||||
return this.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<Parameter> getParameters() {
|
|
||||||
return this.parameters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package pdl.backend.Algorithm;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
import boofcv.struct.image.GrayU8;
|
||||||
|
import boofcv.struct.image.Planar;
|
||||||
|
import pdl.backend.Algorithm.Parameters.Parameter;
|
||||||
|
import pdl.backend.Algorithm.Parameters.ParameterNullPointerException;
|
||||||
|
|
||||||
|
public class Algorithm {
|
||||||
|
private final String name;
|
||||||
|
private final String path;
|
||||||
|
private final List<Parameter> parameters;
|
||||||
|
private final AlgorithmFunctionInterface function;
|
||||||
|
|
||||||
|
public Algorithm(String name, String path, List<Parameter> parameters, AlgorithmFunctionInterface function) {
|
||||||
|
this.name = name;
|
||||||
|
this.path = path;
|
||||||
|
this.parameters = parameters;
|
||||||
|
this.function = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(Planar<GrayU8> image) {
|
||||||
|
List<Object> para = this.parameters.stream().map(p -> p.getValue()).toList();
|
||||||
|
for (Object p : para) {
|
||||||
|
if (p == null) {
|
||||||
|
throw new ParameterNullPointerException("Parameter not set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.function.apply(image, para);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Parameter> getParameters() {
|
||||||
|
return this.parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectNode getNode(ObjectMapper mapper) {
|
||||||
|
ObjectNode node = mapper.createObjectNode();
|
||||||
|
node.put("name", this.getName());
|
||||||
|
node.put("path", this.getPath());
|
||||||
|
ArrayNode parameters = node.putArray("parameters");
|
||||||
|
for (Parameter p : this.getParameters()) {
|
||||||
|
parameters.add(p.getNode(mapper));
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package pdl.backend.Algorithm;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import boofcv.struct.image.GrayU8;
|
||||||
|
import boofcv.struct.image.Planar;
|
||||||
|
|
||||||
|
public interface AlgorithmFunctionInterface {
|
||||||
|
void apply(Planar<GrayU8> image, List<Object> parameters);
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package pdl.backend.Algorithm.Parameters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
public class DoubleParameter extends Parameter {
|
||||||
|
private final double min;
|
||||||
|
private final double max;
|
||||||
|
private final double step;
|
||||||
|
|
||||||
|
public DoubleParameter(String name, String displayName, double min, double max, double step) {
|
||||||
|
super(name, displayName, "range");
|
||||||
|
this.max = max;
|
||||||
|
this.min = min;
|
||||||
|
this.step = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMin() {
|
||||||
|
return this.min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMax() {
|
||||||
|
return this.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getStep() {
|
||||||
|
return this.step;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(String value) {
|
||||||
|
super.setValueObject(Double.parseDouble(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectNode getNode(ObjectMapper mapper) {
|
||||||
|
ObjectNode node = super.getNode(mapper);
|
||||||
|
node.put("min", this.min);
|
||||||
|
node.put("max", this.max);
|
||||||
|
node.put("step", this.step);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package pdl.backend.Algorithm.Parameters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
public class IntegerParameter extends Parameter {
|
||||||
|
private final int min;
|
||||||
|
private final int max;
|
||||||
|
private final int step;
|
||||||
|
|
||||||
|
public IntegerParameter(String name, String displayName, int min, int max, int step) {
|
||||||
|
super(name, displayName, "range");
|
||||||
|
this.max = max;
|
||||||
|
this.min = min;
|
||||||
|
this.step = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMin() {
|
||||||
|
return this.min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMax() {
|
||||||
|
return this.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStep() {
|
||||||
|
return this.step;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(String value) {
|
||||||
|
super.setValueObject(Integer.parseInt(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectNode getNode(ObjectMapper mapper) {
|
||||||
|
ObjectNode node = super.getNode(mapper);
|
||||||
|
node.put("min", this.min);
|
||||||
|
node.put("max", this.max);
|
||||||
|
node.put("step", this.step);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package pdl.backend.Algorithm.Parameters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
public abstract class Parameter {
|
||||||
|
private final String name;
|
||||||
|
private final String displayName;
|
||||||
|
private final String type;
|
||||||
|
private Object value;
|
||||||
|
|
||||||
|
public Parameter(String name, String displayName, String type) {
|
||||||
|
this.name = name;
|
||||||
|
this.displayName = displayName;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return this.displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectNode getNode(ObjectMapper mapper) {
|
||||||
|
ObjectNode node = mapper.createObjectNode();
|
||||||
|
node.put("name", this.name);
|
||||||
|
node.put("displayName", this.displayName);
|
||||||
|
node.put("type", this.type);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValueObject(Object value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package pdl.backend.Algorithm.Parameters;
|
||||||
|
|
||||||
|
public class ParameterNullPointerException extends NullPointerException {
|
||||||
|
public ParameterNullPointerException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package pdl.backend.Algorithm.Parameters;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
public class SelectParameter extends Parameter {
|
||||||
|
private final String[] options;
|
||||||
|
|
||||||
|
public SelectParameter(String name, String displayName, String[] options) {
|
||||||
|
super(name, displayName, "select");
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getOptions() {
|
||||||
|
return this.options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(String value) {
|
||||||
|
super.setValueObject(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectNode getNode(ObjectMapper mapper) {
|
||||||
|
ObjectNode node = super.getNode(mapper);
|
||||||
|
ArrayNode optionsNode = mapper.createArrayNode();
|
||||||
|
for (String option : this.options) {
|
||||||
|
optionsNode.add(option);
|
||||||
|
}
|
||||||
|
node.set("options", optionsNode);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package pdl.backend;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
public class AlgorithmController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper mapper;
|
|
||||||
|
|
||||||
@GetMapping(value = "/algorithms", produces = "application/json")
|
|
||||||
@ResponseBody
|
|
||||||
public ArrayNode getAlgorithmList() {
|
|
||||||
ArrayList<Algorithm> algorithms = new ArrayList<Algorithm>();
|
|
||||||
Algorithm algo = new Algorithm("Changement de luminosité", "changeLuminosity");
|
|
||||||
algorithms.add(algo);
|
|
||||||
algo = new Algorithm("Changement de teinte", "colorFilter");
|
|
||||||
algorithms.add(algo);
|
|
||||||
algo = new Algorithm("Filtre Moyenneur", "meanFilter");
|
|
||||||
algorithms.add(algo);
|
|
||||||
algo = new Algorithm("Filtre Gaussien", "gaussienFilter");
|
|
||||||
algorithms.add(algo);
|
|
||||||
algo = new Algorithm("Détection de contours", "contours");
|
|
||||||
algorithms.add(algo);
|
|
||||||
algo = new Algorithm("Égalisation d'histogramme", "histogram");
|
|
||||||
algorithms.add(algo);
|
|
||||||
|
|
||||||
ArrayNode nodes = mapper.createArrayNode();
|
|
||||||
for (Algorithm a : algorithms) {
|
|
||||||
ObjectNode objectNode = mapper.createObjectNode();
|
|
||||||
objectNode.put("name", a.getName());
|
|
||||||
objectNode.put("path", a.getPath());
|
|
||||||
ArrayNode parameters = objectNode.putArray("parameters");
|
|
||||||
for (Parameter p : a.getParameters()) {
|
|
||||||
ObjectNode paramNode = mapper.createObjectNode();
|
|
||||||
paramNode.put("name", p.getName());
|
|
||||||
paramNode.put("displayName", p.getDisplayName());
|
|
||||||
paramNode.put("min", p.getMin());
|
|
||||||
paramNode.put("max", p.getMax());
|
|
||||||
paramNode.put("step", p.getStep());
|
|
||||||
parameters.add(paramNode);
|
|
||||||
}
|
|
||||||
nodes.add(objectNode);
|
|
||||||
}
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package pdl.backend.Controller;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
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.Algorithm.Algorithm;
|
||||||
|
import pdl.backend.Algorithm.Parameters.DoubleParameter;
|
||||||
|
import pdl.backend.Algorithm.Parameters.IntegerParameter;
|
||||||
|
import pdl.backend.Algorithm.Parameters.SelectParameter;
|
||||||
|
import pdl.backend.Image.ImageProcessing;
|
||||||
|
import pdl.backend.Algorithm.Parameters.Parameter;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class AlgorithmController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper mapper;
|
||||||
|
|
||||||
|
public static List<Algorithm> algorithmList() {
|
||||||
|
final String[] borderTypes = { "SKIP", "ZERO", "NORMALIZED", "REFLECT", "EXTENDED", "WRAP" };
|
||||||
|
ArrayList<Algorithm> algorithms = new ArrayList<Algorithm>();
|
||||||
|
algorithms.add(new Algorithm("Changement de luminosité", "changeLuminosity",
|
||||||
|
Arrays.asList(new IntegerParameter("delta", "delta", -255, 255, 1)),
|
||||||
|
(Planar<GrayU8> input, List<Object> para) -> {
|
||||||
|
ImageProcessing.changeLuminosity(input, (int) para.get(0));
|
||||||
|
}));
|
||||||
|
algorithms.add(new Algorithm("Changement de teinte", "colorFilter",
|
||||||
|
Arrays.asList(new IntegerParameter("hue", "Teinte", 0, 359, 1)),
|
||||||
|
(Planar<GrayU8> input, List<Object> para) -> {
|
||||||
|
ImageProcessing.colorFilter(input, (int) para.get(0));
|
||||||
|
}));
|
||||||
|
|
||||||
|
// TODO: TESTS
|
||||||
|
algorithms.add(new Algorithm("Filtre Moyenneur", "meanFilter", Arrays.asList(
|
||||||
|
new IntegerParameter("size", "Taille du filtre", 1, 21, 2),
|
||||||
|
new SelectParameter("borderType", "Type de bordure", borderTypes)),
|
||||||
|
(Planar<GrayU8> input, List<Object> para) -> {
|
||||||
|
ImageProcessing.meanFilter(input, (int) para.get(0), BorderType.valueOf((String) para.get(1)));
|
||||||
|
}));
|
||||||
|
|
||||||
|
// TODO: TESTS
|
||||||
|
algorithms.add(new Algorithm("Filtre Gaussien", "gaussienFilter", Arrays.asList(
|
||||||
|
new IntegerParameter("size", "Taille du filtre", 1, 21, 2),
|
||||||
|
new DoubleParameter("sigma", "Sigma", 0.1, 2, 0.1),
|
||||||
|
new SelectParameter("borderType", "Type de bordure", borderTypes)),
|
||||||
|
(Planar<GrayU8> input, List<Object> para) -> {
|
||||||
|
ImageProcessing.gaussienFilter(input, (int) para.get(0), (double) para.get(1),
|
||||||
|
BorderType.valueOf((String) para.get(2)));
|
||||||
|
}));
|
||||||
|
|
||||||
|
algorithms.add(new Algorithm("Détection de contours", "contours", new ArrayList<Parameter>(),
|
||||||
|
(Planar<GrayU8> input, List<Object> para) -> {
|
||||||
|
ImageProcessing.gradientImageSobel(input);
|
||||||
|
}));
|
||||||
|
|
||||||
|
algorithms.add(new Algorithm("Égalisation d'histogramme", "histogram", new ArrayList<Parameter>(),
|
||||||
|
(Planar<GrayU8> input, List<Object> para) -> {
|
||||||
|
ImageProcessing.histogram(input);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return algorithms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/algorithms", produces = "application/json")
|
||||||
|
@ResponseBody
|
||||||
|
public ArrayNode getAlgorithmList() {
|
||||||
|
List<Algorithm> algorithms = AlgorithmController.algorithmList();
|
||||||
|
ArrayNode nodes = mapper.createArrayNode();
|
||||||
|
for (Algorithm a : algorithms) {
|
||||||
|
nodes.add(a.getNode(mapper));
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+38
-72
@@ -1,4 +1,4 @@
|
|||||||
package pdl.backend;
|
package pdl.backend.Controller;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -16,8 +16,13 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
|
|||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
import boofcv.io.image.ConvertBufferedImage;
|
import boofcv.io.image.ConvertBufferedImage;
|
||||||
|
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.Algorithm.Algorithm;
|
||||||
|
import pdl.backend.Algorithm.Parameters.Parameter;
|
||||||
|
import pdl.backend.Image.Image;
|
||||||
|
import pdl.backend.Image.ImageDao;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
@@ -135,6 +140,23 @@ public class ImageController {
|
|||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BorderType getBorderType(String borderType) {
|
||||||
|
switch (borderType) {
|
||||||
|
case "EXTENDED":
|
||||||
|
return BorderType.EXTENDED;
|
||||||
|
case "REFLECT":
|
||||||
|
return BorderType.REFLECT;
|
||||||
|
case "REFLECT_101":
|
||||||
|
return BorderType.NORMALIZED;
|
||||||
|
case "WRAP":
|
||||||
|
return BorderType.WRAP;
|
||||||
|
case "ZERO":
|
||||||
|
return BorderType.ZERO;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* récupère une image et la modifie si un algorithme est renseigné
|
* récupère une image et la modifie si un algorithme est renseigné
|
||||||
*
|
*
|
||||||
@@ -159,6 +181,7 @@ public class ImageController {
|
|||||||
return ResponseEntity.ok().contentType(image.getMediaType())
|
return ResponseEntity.ok().contentType(image.getMediaType())
|
||||||
.body(new InputStreamResource(inputStream));
|
.body(new InputStreamResource(inputStream));
|
||||||
}
|
}
|
||||||
|
final List<Algorithm> algos = AlgorithmController.algorithmList();
|
||||||
// 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);
|
||||||
@@ -173,82 +196,25 @@ public class ImageController {
|
|||||||
}
|
}
|
||||||
Planar<GrayU8> input = ConvertBufferedImage.convertFromPlanar(bufImg, null, true, GrayU8.class);
|
Planar<GrayU8> input = ConvertBufferedImage.convertFromPlanar(bufImg, null, true, GrayU8.class);
|
||||||
|
|
||||||
// applique l'algo a l'image en fonction des parametres et verifie la validite
|
for (Algorithm a : algos) {
|
||||||
// des parametres
|
if (a.getPath().equals(algo)) {
|
||||||
switch (algo) {
|
for (Parameter p : a.getParameters()) {
|
||||||
case "changeLuminosity":
|
if (!parameters.containsKey(p.getName())) {
|
||||||
if (!parameters.containsKey("delta") || parameters.size() != 2)
|
return JSONError("Parameter '" + p.getName() + "' is missing", HttpStatus.BAD_REQUEST);
|
||||||
return JSONError("Algorithm 'changeLuminosity' requires one parameter 'delta'(integer)",
|
}
|
||||||
HttpStatus.BAD_REQUEST);
|
try {
|
||||||
try {
|
p.setValue(parameters.get(p.getName()));
|
||||||
int i = Integer.parseInt(parameters.get("delta"));
|
} catch (Exception e) {
|
||||||
ImageProcessing.changeLuminosity(input, i);
|
return JSONError("Parameter '" + p.getName() + "' is invalid", HttpStatus.BAD_REQUEST);
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return JSONError("Parameter 'delta' should be an integer", HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "histogram":
|
|
||||||
if (parameters.size() != 1)
|
|
||||||
return JSONError("Algorithm 'histogram' doesn't require any parameters", HttpStatus.BAD_REQUEST);
|
|
||||||
ImageProcessing.histogram(input);
|
|
||||||
break;
|
|
||||||
case "colorFilter":
|
|
||||||
if (!parameters.containsKey("hue") || parameters.size() != 2)
|
|
||||||
return JSONError("Algorithm 'colorFilter' requires one parameter 'hue'(integer)",
|
|
||||||
HttpStatus.BAD_REQUEST);
|
|
||||||
try {
|
|
||||||
int i = Integer.parseInt(parameters.get("hue"));
|
|
||||||
if (i < 0) {
|
|
||||||
return JSONError("Parameter 'hue' should be an integer between 0 and 360",
|
|
||||||
HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
}
|
||||||
ImageProcessing.colorFilter(i, input);
|
|
||||||
// hue entre 0 et 360?
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return JSONError("Parameter 'hue' should be an integer between 0 and 360",
|
|
||||||
HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case "meanFilter":
|
|
||||||
if (!parameters.containsKey("size") || parameters.size() != 2)
|
|
||||||
return JSONError("Algorithm 'meanFilter' requires one parameter 'size'(positive odd integer)",
|
|
||||||
HttpStatus.BAD_REQUEST);
|
|
||||||
try {
|
try {
|
||||||
int i = Integer.parseInt(parameters.get("size"));
|
a.apply(input);
|
||||||
if (i % 2 == 0)
|
} catch (Exception e) {
|
||||||
return JSONError("Parameter 'size' should be a positive odd integer",
|
return JSONError("There was an internal server error", HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
HttpStatus.BAD_REQUEST);
|
|
||||||
Planar<GrayU8> clone = input.clone();
|
|
||||||
ImageProcessing.meanFilter(clone, input, i);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return JSONError("Parameter 'size' should be a positive odd integer",
|
|
||||||
HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "gaussienFilter":
|
}
|
||||||
if (/* !parameters.containsKey("size") || */ parameters.size() != 1)
|
|
||||||
return JSONError("Algorithm 'gaussienFilter' requires no parameters", HttpStatus.BAD_REQUEST);
|
|
||||||
try {
|
|
||||||
// int i = Integer.parseInt(parameters.get("size"));
|
|
||||||
// if (i % 2 == 0)
|
|
||||||
// return JSONError("Parameter 'size' should be a positive odd integer",
|
|
||||||
// HttpStatus.BAD_REQUEST);
|
|
||||||
Planar<GrayU8> clone = input.clone();
|
|
||||||
ImageProcessing.gaussienFilter(clone, input, 5);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return JSONError("Parameter 'size' should be a positive odd integer",
|
|
||||||
HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "contours":
|
|
||||||
if (parameters.size() != 1)
|
|
||||||
return JSONError("Algorithm 'contours' doesn't require any parameters", HttpStatus.BAD_REQUEST);
|
|
||||||
Planar<GrayU8> clone = input.clone();
|
|
||||||
ImageProcessing.gradientImageSobel(clone, input);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return JSONError("Requested algorithm doesn't exist", HttpStatus.BAD_REQUEST);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// transforme l'image en quelque chose que le navigateur puisse lire
|
// transforme l'image en quelque chose que le navigateur puisse lire
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package pdl.backend;
|
package pdl.backend.Image;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
+4
-2
@@ -1,4 +1,4 @@
|
|||||||
package pdl.backend;
|
package pdl.backend.Image;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -13,6 +13,8 @@ import java.util.Optional;
|
|||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import pdl.backend.Dao;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public class ImageDao implements Dao<Image> {
|
public class ImageDao implements Dao<Image> {
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ public class ImageDao implements Dao<Image> {
|
|||||||
private final Map<Long, Image> images = new HashMap<>();
|
private final Map<Long, Image> images = new HashMap<>();
|
||||||
|
|
||||||
public ImageDao() throws IOException {
|
public ImageDao() throws IOException {
|
||||||
this("images");
|
this("/images");
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageDao(final String folderPathStr) throws IOException {
|
public ImageDao(final String folderPathStr) throws IOException {
|
||||||
+132
-80
@@ -1,5 +1,6 @@
|
|||||||
package pdl.backend;
|
package pdl.backend.Image;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -9,7 +10,10 @@ import java.util.Random;
|
|||||||
|
|
||||||
import boofcv.concurrency.BoofConcurrency;
|
import boofcv.concurrency.BoofConcurrency;
|
||||||
|
|
||||||
public class ImageProcessing {
|
public final class ImageProcessing {
|
||||||
|
|
||||||
|
private ImageProcessing() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -46,15 +50,14 @@ public class ImageProcessing {
|
|||||||
*
|
*
|
||||||
* Convertit une image en couleur en une image en niveaux de gris
|
* Convertit une image en couleur en une image en niveaux de gris
|
||||||
*
|
*
|
||||||
* @param input l'image d'entrée en couleur
|
* @param input l'image d'entrée en couleur
|
||||||
* @param output l'image de sortie en niveaux de gris
|
|
||||||
*/
|
*/
|
||||||
public static void convertColorToGray(Planar<GrayU8> input, Planar<GrayU8> output) {
|
public static void convertColorToGray(Planar<GrayU8> input) {
|
||||||
BoofConcurrency.loopFor(0, input.height, y -> {
|
BoofConcurrency.loopFor(0, input.height, y -> {
|
||||||
for (int x = 0; x < input.width; x++) {
|
for (int x = 0; x < input.width; x++) {
|
||||||
int[] rgb = getRGBValue(input, x, y);
|
int[] rgb = getRGBValue(input, x, y);
|
||||||
int gray = (int) (0.3 * rgb[0] + 0.59 * rgb[1] + 0.11 * rgb[2]);
|
int gray = (int) (0.3 * rgb[0] + 0.59 * rgb[1] + 0.11 * rgb[2]);
|
||||||
setRGBValue(output, x, y, new int[] { gray, gray, gray });
|
setRGBValue(input, x, y, new int[] { gray, gray, gray });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -93,8 +96,8 @@ public class ImageProcessing {
|
|||||||
*/
|
*/
|
||||||
public static void histogram(Planar<GrayU8> input) {
|
public static void histogram(Planar<GrayU8> input) {
|
||||||
int h[] = new int[256];
|
int h[] = new int[256];
|
||||||
Planar<GrayU8> grayInput = input.createSameShape();
|
Planar<GrayU8> grayInput = input.clone();
|
||||||
convertColorToGray(input, grayInput);
|
convertColorToGray(grayInput);
|
||||||
// On remplit le tableau avec le nombre de pixels pour chaque niveau de
|
// On remplit le tableau avec le nombre de pixels pour chaque niveau de
|
||||||
// gris
|
// gris
|
||||||
BoofConcurrency.loopFor(0, input.height, y -> {
|
BoofConcurrency.loopFor(0, input.height, y -> {
|
||||||
@@ -218,7 +221,7 @@ public class ImageProcessing {
|
|||||||
* @param hue La valeur de la teinte à appliquer
|
* @param hue La valeur de la teinte à appliquer
|
||||||
* @param input L'image à modifier
|
* @param input L'image à modifier
|
||||||
*/
|
*/
|
||||||
public static void colorFilter(int hue, Planar<GrayU8> input) {
|
public static void colorFilter(Planar<GrayU8> input, int hue) {
|
||||||
if (hue < 0 || hue > 360) {
|
if (hue < 0 || hue > 360) {
|
||||||
throw new IllegalArgumentException("Hue doit être compris entre 0 et 360");
|
throw new IllegalArgumentException("Hue doit être compris entre 0 et 360");
|
||||||
}
|
}
|
||||||
@@ -240,15 +243,15 @@ public class ImageProcessing {
|
|||||||
* Calcule et stocke dans output l'image de la norme du gradient
|
* Calcule et stocke dans output l'image de la norme du gradient
|
||||||
* calculé sur input par convolution avec les filtres de Sobel.
|
* calculé sur input par convolution avec les filtres de Sobel.
|
||||||
*
|
*
|
||||||
* @param input L'image d'entrée en niveaux de gris.
|
* @param input L'image d'entrée en niveaux de gris.
|
||||||
* @param output L'image de sortie en niveaux de gris.
|
|
||||||
*/
|
*/
|
||||||
public static void gradientImageSobel(Planar<GrayU8> input, Planar<GrayU8> output) {
|
public static void gradientImageSobel(Planar<GrayU8> input) {
|
||||||
|
Planar<GrayU8> output = input.createSameShape();
|
||||||
int h1[][] = { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } };
|
int h1[][] = { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } };
|
||||||
int h2[][] = { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };
|
int h2[][] = { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };
|
||||||
int half = h1.length / 2;
|
int half = h1.length / 2;
|
||||||
Planar<GrayU8> inputGray = input.createSameShape();
|
Planar<GrayU8> inputGray = input.clone();
|
||||||
convertColorToGray(input, inputGray);
|
convertColorToGray(inputGray);
|
||||||
BoofConcurrency.loopFor(0, input.height, y -> {
|
BoofConcurrency.loopFor(0, input.height, y -> {
|
||||||
for (int x = 0; x < input.width; x++) {
|
for (int x = 0; x < input.width; x++) {
|
||||||
if (y < half || x < half || y >= input.height - half || x >= input.width - half) {
|
if (y < half || x < half || y >= input.height - half || x >= input.width - half) {
|
||||||
@@ -279,78 +282,84 @@ public class ImageProcessing {
|
|||||||
setRGBValue(output, x, y, rgb);
|
setRGBValue(output, x, y, rgb);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
for (int i = 0; i < input.width; i++) {
|
||||||
|
for (int j = 0; j < input.height; j++) {
|
||||||
|
int[] rgb = getRGBValue(output, i, j);
|
||||||
|
setRGBValue(input, i, j, rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applique un filtre moyenneur de taille indiquée à l'image
|
* Applique un filtre moyenneur de taille indiquée à l'image
|
||||||
* passée en paramètre (le bord de l'image n'est pas modifié)
|
* passée en paramètre et stocke le résultat dans l'image de sortie.
|
||||||
*
|
*
|
||||||
* @param input image d'entrée
|
* @param input image d'entrée
|
||||||
* @param output image de sortie
|
* @param size taille du filtre à appliquer (doit être impaire)
|
||||||
* @param size taille du filtre à appliquer (doit être impaire)
|
* @param borderType type de bord à utiliser pour la convolution
|
||||||
*/
|
*/
|
||||||
public static void meanFilter(Planar<GrayU8> input, Planar<GrayU8> output, int size) {
|
public static void meanFilter(Planar<GrayU8> input, int size, BorderType borderType) {
|
||||||
// Vérification que la taille est impaire
|
// Vérification que la taille est impaire
|
||||||
if (size % 2 == 0) {
|
if (size % 2 == 0 || size < 0) {
|
||||||
throw new IllegalArgumentException("La taille doit être impaire");
|
throw new IllegalArgumentException(
|
||||||
|
"La taille doit être impaire et positive");
|
||||||
}
|
}
|
||||||
// Boucle pour parcourir tous les pixels de l'image d'entrée
|
int[][] kernel = new int[size][size];
|
||||||
BoofConcurrency.loopFor(0, input.height, y -> {
|
for (int i = 0; i < size; i++) {
|
||||||
for (int x = 0; x < input.width; x++) {
|
for (int j = 0; j < size; j++) {
|
||||||
if (y < size / 2 || x < size / 2 || y >= input.height - size / 2
|
kernel[i][j] = 1;
|
||||||
|| x >= input.width - size / 2) {
|
|
||||||
setRGBValue(output, x, y, getRGBValue(input, x, y));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int totalValue[] = new int[3];
|
|
||||||
// Boucle pour parcourir le carré size x size autour du pixel actuel
|
|
||||||
for (int j = y - size / 2; j < y + size / 2 + 1; j++) {
|
|
||||||
for (int i = x - size / 2; i < x + size / 2 + 1; i++) {
|
|
||||||
for (int k = 0; k < totalValue.length; k++) {
|
|
||||||
totalValue[k] += input.getBand(k).get(i, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Calcule la moyenne des pixels du carré et l'applique au pixel actuel dans
|
|
||||||
// l'image de
|
|
||||||
// sortie
|
|
||||||
for (int k = 0; k < totalValue.length; k++) {
|
|
||||||
totalValue[k] /= size * size;
|
|
||||||
}
|
|
||||||
setRGBValue(output, x, y, totalValue);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
convolution(input, kernel, borderType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
|
||||||
* Calcule le noyau gaussien de taille demandée
|
|
||||||
*
|
*
|
||||||
* @param size taille du noyau gaussien
|
* Applique un filtre gaussien de taille et de valeur données sur une image et
|
||||||
* @return noyau gaussien
|
* stocke le résultat dans l'image de sortie.
|
||||||
|
*
|
||||||
|
* @param input L'image d'entrée
|
||||||
|
* @param size La taille du filtre à appliquer (doit être impaire)
|
||||||
|
* @param sigma La valeur du sigma à utiliser pour le filtre gaussien.
|
||||||
|
* @param borderType Le type de bord à utiliser pour la convolution
|
||||||
*/
|
*/
|
||||||
public static int[][] createGaussienKernel(int size) {
|
public static void gaussienFilter(Planar<GrayU8> input, int size, double sigma, BorderType borderType) {
|
||||||
// Vérification que la taille est impaire
|
if (size % 2 == 0 || size < 0 || sigma <= 0) {
|
||||||
if (size % 2 == 0 || size < 0) {
|
throw new IllegalArgumentException(
|
||||||
throw new IllegalArgumentException("La taille doit être impaire et positive");
|
"La taille doit être impaire et positive et sigma doit être strictement positif");
|
||||||
}
|
}
|
||||||
double[][] kernel = new double[size][size];
|
double[][] kernel = new double[size][size];
|
||||||
double sigma = 4 / 3;
|
int half = size / 2;
|
||||||
double sum = 0.0; // For accumulating the kernel values
|
double sum = 0;
|
||||||
for (int x = 0; x < size; ++x)
|
for (int x = -half; x <= half; ++x) {
|
||||||
for (int y = 0; y < size; ++y) {
|
for (int y = -half; y <= half; ++y) {
|
||||||
kernel[x][y] = 1 / (2 * Math.PI * sigma * sigma) * Math.exp(-(x * x + y * y) / (2 * sigma * sigma));
|
double gaussianValue = 1 / (2 * Math.PI * sigma * sigma)
|
||||||
|
* Math.exp(-(x * x + y * y) / (2 * sigma * sigma));
|
||||||
|
kernel[x + half][y + half] = gaussianValue;
|
||||||
|
sum += gaussianValue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// On récupère le pourcentage de chaque valeur par rapport à la somme
|
||||||
|
for (int x = -half; x <= half; ++x) {
|
||||||
|
for (int y = -half; y <= half; ++y) {
|
||||||
|
kernel[x + half][y + half] = kernel[x + half][y + half] / sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize the kernel
|
int[][] kernelInt = new int[size][size];
|
||||||
int[][] kernelk = new int[size][size];
|
double factor = 1 / kernel[0][0];
|
||||||
for (int x = 0; x < size; ++x)
|
// Si le facteur est trop grand (la valeur du coin est très très petite par
|
||||||
for (int y = 0; y < size; ++y)
|
// rapport au reste), on le fixe à 1 million
|
||||||
kernelk[x][y] = (int) (kernel[x][y] / sum);
|
if (factor > 1000000) {
|
||||||
|
factor = 1000000;
|
||||||
return kernelk;
|
}
|
||||||
|
// normalisation
|
||||||
|
for (int x = 0; x < size; ++x) {
|
||||||
|
for (int y = 0; y < size; ++y) {
|
||||||
|
kernelInt[x][y] = (int) (kernel[x][y] * factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
convolution(input, kernelInt, borderType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int[][] createGaussienKernel(int size, double sigma) {
|
public static int[][] createGaussienKernel(int size, double sigma) {
|
||||||
@@ -397,15 +406,14 @@ public class ImageProcessing {
|
|||||||
* Effectue une convolution sur une image avec le noyau
|
* Effectue une convolution sur une image avec le noyau
|
||||||
* donné et stocke le résultat dans l'image de sortie.
|
* donné et stocke le résultat dans l'image de sortie.
|
||||||
*
|
*
|
||||||
* @param input L'image d'entrée
|
* @param input L'image d'entrée
|
||||||
* @param output L'image de sortie
|
* @param kernel Le noyau de convolution à utiliser.
|
||||||
* @param kernel Le noyau de convolution à utiliser.
|
* @param borderType Le type de bord à utiliser pour la convolution
|
||||||
*/
|
*/
|
||||||
public static void gaussienFilter(Planar<GrayU8> input, Planar<GrayU8> output,
|
public static void convolution(Planar<GrayU8> input, int[][] kernel, BorderType borderType) {
|
||||||
int size) {
|
Planar<GrayU8> copy = input.clone();
|
||||||
int[][] kernel = { { 1, 2, 3, 2, 1 }, { 2, 6, 8, 6, 2 }, { 3, 8, 10, 8, 3 }, { 2, 6, 8, 6, 2 },
|
int size = kernel.length;
|
||||||
{ 1, 2, 3, 2, 1 } };
|
int half = kernel.length / 2;
|
||||||
int half = size / 2;
|
|
||||||
int coefs = 0;
|
int coefs = 0;
|
||||||
// Calcul du nombre total de coefficients dans le noyau
|
// Calcul du nombre total de coefficients dans le noyau
|
||||||
for (int i = 0; i < kernel.length; i++) {
|
for (int i = 0; i < kernel.length; i++) {
|
||||||
@@ -417,10 +425,54 @@ public class ImageProcessing {
|
|||||||
// Parcours de l'image d'entrée et application de la convolution
|
// Parcours de l'image d'entrée et application de la convolution
|
||||||
BoofConcurrency.loopFor(0, input.height, y -> {
|
BoofConcurrency.loopFor(0, input.height, y -> {
|
||||||
for (int x = 0; x < input.width; x++) {
|
for (int x = 0; x < input.width; x++) {
|
||||||
// Vérification si le carré sera à l'intérieur de l'image
|
// On traite d'abord les bords
|
||||||
if (y < half || x < half || y >= input.height - half || x >= input.width -
|
if (y < half || x < half || y >= input.height - half || x >= input.width -
|
||||||
half) {
|
half) {
|
||||||
setRGBValue(output, x, y, getRGBValue(input, x, y));
|
if (borderType == BorderType.SKIP) {
|
||||||
|
setRGBValue(input, x, y, getRGBValue(copy, x, y));
|
||||||
|
} else if (borderType == BorderType.ZERO) {
|
||||||
|
setRGBValue(input, x, y, new int[] { 0, 0, 0 });
|
||||||
|
} else {
|
||||||
|
int totalValue[] = new int[3];
|
||||||
|
int coefsUsed = 0;
|
||||||
|
for (int j = y - half; j < y + half + 1; j++) {
|
||||||
|
int tmpj = j;
|
||||||
|
if (j < 0 || j >= input.height) {
|
||||||
|
if (borderType == BorderType.EXTENDED) {
|
||||||
|
tmpj = j < 0 ? 0 : input.height - 1;
|
||||||
|
} else if (borderType == BorderType.NORMALIZED) {
|
||||||
|
continue;
|
||||||
|
} else if (borderType == BorderType.REFLECT) {
|
||||||
|
tmpj = j < 0 ? -j : (input.height - 1) - (j - (input.height - 1));
|
||||||
|
} else if (borderType == BorderType.WRAP) {
|
||||||
|
tmpj = j < 0 ? j + (input.height - 1) : (j - (input.height - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = x - size / 2; i < x + size / 2 + 1; i++) {
|
||||||
|
int tmpi = i;
|
||||||
|
if (i < 0 || i >= input.width) {
|
||||||
|
if (borderType == BorderType.EXTENDED) {
|
||||||
|
tmpi = i < 0 ? 0 : input.width - 1;
|
||||||
|
} else if (borderType == BorderType.NORMALIZED) {
|
||||||
|
continue;
|
||||||
|
} else if (borderType == BorderType.REFLECT) {
|
||||||
|
tmpi = i < 0 ? -i : (input.width - 1) - (i - (input.width - 1));
|
||||||
|
} else if (borderType == BorderType.WRAP) {
|
||||||
|
tmpi = i < 0 ? i + (input.width - 1) : (i - (input.width - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int k = 0; k < totalValue.length; k++) {
|
||||||
|
totalValue[k] += copy.getBand(k).get(tmpi, tmpj)
|
||||||
|
* kernel[i + (-x + half)][j + (-y + half)];
|
||||||
|
}
|
||||||
|
coefsUsed += kernel[i + (-x + half)][j + (-y + half)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int k = 0; k < totalValue.length; k++) {
|
||||||
|
totalValue[k] /= coefsUsed;
|
||||||
|
}
|
||||||
|
setRGBValue(input, x, y, totalValue);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int currentJ = 0;
|
int currentJ = 0;
|
||||||
@@ -430,7 +482,7 @@ public class ImageProcessing {
|
|||||||
for (int j = y - half; j <= y + half; j++) {
|
for (int j = y - half; j <= y + half; j++) {
|
||||||
for (int i = x - half; i <= x + half; i++) {
|
for (int i = x - half; i <= x + half; i++) {
|
||||||
for (int k = 0; k < totalValue.length; k++) {
|
for (int k = 0; k < totalValue.length; k++) {
|
||||||
totalValue[k] += input.getBand(k).get(i, j) * kernel[currentJ][currentI];
|
totalValue[k] += copy.getBand(k).get(i, j) * kernel[currentJ][currentI];
|
||||||
}
|
}
|
||||||
currentI++;
|
currentI++;
|
||||||
}
|
}
|
||||||
@@ -440,7 +492,7 @@ public class ImageProcessing {
|
|||||||
for (int k = 0; k < totalValue.length; k++) {
|
for (int k = 0; k < totalValue.length; k++) {
|
||||||
totalValue[k] /= totalCoefs;
|
totalValue[k] /= totalCoefs;
|
||||||
}
|
}
|
||||||
setRGBValue(output, x, y, totalValue);
|
setRGBValue(input, x, y, totalValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package pdl.backend;
|
package pdl.backend.Image;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package pdl.backend;
|
|
||||||
|
|
||||||
public class Parameter {
|
|
||||||
private final String name;
|
|
||||||
private final String displayName;
|
|
||||||
private final int min;
|
|
||||||
private final int max;
|
|
||||||
private final int step;
|
|
||||||
|
|
||||||
public Parameter(String name, String displayName, int min, int max, int step) {
|
|
||||||
this.name = name;
|
|
||||||
this.displayName = displayName;
|
|
||||||
this.max = max;
|
|
||||||
this.min = min;
|
|
||||||
this.step = step;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return this.displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMin() {
|
|
||||||
return this.min;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMax() {
|
|
||||||
return this.max;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getStep() {
|
|
||||||
return this.step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,6 +22,9 @@ import org.springframework.test.util.ReflectionTestUtils;
|
|||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import pdl.backend.Image.Image;
|
||||||
|
import pdl.backend.Image.ImageDao;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
@AutoConfigureMockMvc
|
@AutoConfigureMockMvc
|
||||||
@TestMethodOrder(OrderAnnotation.class)
|
@TestMethodOrder(OrderAnnotation.class)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import java.io.IOException;
|
|||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import pdl.backend.Image.ImageDao;
|
||||||
|
|
||||||
public class ImageDaoTests {
|
public class ImageDaoTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+3
-44
@@ -3,6 +3,7 @@ import Home from './components/Home.vue'
|
|||||||
import Gallery from './components/Gallery.vue';
|
import Gallery from './components/Gallery.vue';
|
||||||
import NotFound from './components/NotFound.vue'
|
import NotFound from './components/NotFound.vue'
|
||||||
import Upload from './components/Upload.vue'
|
import Upload from './components/Upload.vue'
|
||||||
|
import StableDiff from './components/StableDiff.vue';
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { api } from '@/http-api'
|
import { api } from '@/http-api'
|
||||||
import { ImageType, ImageClass } from './image';
|
import { ImageType, ImageClass } from './image';
|
||||||
@@ -48,6 +49,7 @@ export default defineComponent({
|
|||||||
<li><router-link :to="{ name: 'home' }">Home</router-link></li>
|
<li><router-link :to="{ name: 'home' }">Home</router-link></li>
|
||||||
<li><router-link :to="{ name: 'gallery' }">Gallery</router-link></li>
|
<li><router-link :to="{ name: 'gallery' }">Gallery</router-link></li>
|
||||||
<li><router-link :to="{ name: 'upload' }">Upload</router-link></li>
|
<li><router-link :to="{ name: 'upload' }">Upload</router-link></li>
|
||||||
|
<li><router-link :to="{ name: 'generate' }">Generate Images</router-link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,12 +57,7 @@ export default defineComponent({
|
|||||||
<RouterView :images="images" @delete="deleteFile" @updateImageList="updateImageList()" />
|
<RouterView :images="images" @delete="deleteFile" @updateImageList="updateImageList()" />
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.navi {
|
@import url("@/navi.css");
|
||||||
/* reduce size of children */
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
header {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -72,17 +69,6 @@ header {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul>li+li {
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul>li {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.router-link-active {
|
.router-link-active {
|
||||||
background-color: #646cff;
|
background-color: #646cff;
|
||||||
}
|
}
|
||||||
@@ -90,31 +76,4 @@ ul>li {
|
|||||||
.router-link-active>a {
|
.router-link-active>a {
|
||||||
color: rgb(205, 201, 194);
|
color: rgb(205, 201, 194);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
padding: 5px 15px;
|
|
||||||
border-radius: 60px;
|
|
||||||
color: rgb(172, 165, 154);
|
|
||||||
text-decoration: none;
|
|
||||||
font-family: "Aeonik", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
padding-left: 20px;
|
|
||||||
height: 60px;
|
|
||||||
padding-right: 20px;
|
|
||||||
border-radius: 60px;
|
|
||||||
background-color: rgb(24, 26, 27);
|
|
||||||
margin-right: 20px;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
list-style: none;
|
|
||||||
padding-inline-start: 0;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@@ -1,3 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<div class="custom-select" @blur="open = false">
|
||||||
|
<div class="selected" :class="{ open: open }" @click="open = !open">
|
||||||
|
<span>{{ list[selected] }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="items" :class="{ selectHide: !open }">
|
||||||
|
<div v-for="(name, index) in list" @click="open = false; $emit('updateSelected', index)">
|
||||||
|
<span>{{ name }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
</script>
|
||||||
|
<script lang="ts">
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
list: {
|
||||||
|
type: Array as () => string[],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
updateSelected(id: number) {
|
||||||
|
return id >= 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
open: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
.custom-select {
|
.custom-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -68,4 +112,5 @@
|
|||||||
|
|
||||||
.selectHide {
|
.selectHide {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
@@ -19,7 +19,7 @@ export default defineComponent({
|
|||||||
<div class="flex" v-if="images.size > 0">
|
<div class="flex" v-if="images.size > 0">
|
||||||
<div v-for="[id, image] in images" :key="id" class="image">
|
<div v-for="[id, image] in images" :key="id" class="image">
|
||||||
<label>{{ image.name }}</label>
|
<label>{{ image.name }}</label>
|
||||||
<Image class="gallery" :id="id" :images="images" redirect="home" />
|
<Image :id="id" :images="images" redirect="home" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h1 v-else>Aucune image trouvée</h1>
|
<h1 v-else>Aucune image trouvée</h1>
|
||||||
@@ -59,9 +59,8 @@ label {
|
|||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
<style>
|
:deep(.imageContainer) {
|
||||||
.gallery.imageContainer {
|
|
||||||
margin: 2vw;
|
margin: 2vw;
|
||||||
max-height: min(33vh, 33vw);
|
max-height: min(33vh, 33vw);
|
||||||
max-width: min(33vw, 33vh);
|
max-width: min(33vw, 33vh);
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export default defineComponent({
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
imageSelectedId: -1,
|
imageSelectedId: -1,
|
||||||
timer: null as ReturnType<typeof setTimeout> | null,
|
|
||||||
editor: false,
|
editor: false,
|
||||||
filter: undefined as Filter | undefined,
|
filter: undefined as Filter | undefined,
|
||||||
}
|
}
|
||||||
@@ -38,6 +37,9 @@ export default defineComponent({
|
|||||||
'images.size': {
|
'images.size': {
|
||||||
handler() { this.updateImageSelectedId(); },
|
handler() { this.updateImageSelectedId(); },
|
||||||
},
|
},
|
||||||
|
imageSelectedId() {
|
||||||
|
this.filter = undefined;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateImageSelectedId(id: number = -1) {
|
updateImageSelectedId(id: number = -1) {
|
||||||
@@ -47,35 +49,29 @@ export default defineComponent({
|
|||||||
this.imageSelectedId = id;
|
this.imageSelectedId = id;
|
||||||
else
|
else
|
||||||
this.imageSelectedId = this.images.keys().next().value;
|
this.imageSelectedId = this.images.keys().next().value;
|
||||||
},
|
|
||||||
startTimer() {
|
|
||||||
if (this.timer !== null)
|
|
||||||
clearTimeout(this.timer);
|
|
||||||
this.timer = setTimeout(() => {
|
|
||||||
this.editor = !this.editor;
|
|
||||||
}, 1900);
|
|
||||||
},
|
|
||||||
clearTimer() {
|
|
||||||
if (this.timer !== null) {
|
|
||||||
clearTimeout(this.timer);
|
|
||||||
this.timer = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex" v-if="images.size > 0">
|
<div class="flex" v-if="images.size > 0">
|
||||||
<h1 v-if="!editor">MODE SÉLECTION</h1>
|
<div class="navi">
|
||||||
<h1 v-else>MODE ÉDITEUR</h1>
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<h3 :class="{ active: !editor }" @click="editor = false">Mode sélection</h3>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<h3 :class="{ active: editor }" @click="editor = true">Mode éditeur</h3>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
<HomeSelect v-if="!editor" :images="images" :image-selected-id="imageSelectedId" @delete="id => $emit('delete', id)"
|
<HomeSelect v-if="!editor" :images="images" :image-selected-id="imageSelectedId" @delete="id => $emit('delete', id)"
|
||||||
@updateImageSelectedId="id => updateImageSelectedId(id)"></HomeSelect>
|
@updateImageSelectedId="id => updateImageSelectedId(id)"></HomeSelect>
|
||||||
<HomeEditor v-else @apply-filter="(newFilter) => filter = newFilter"></HomeEditor>
|
<HomeEditor v-else @apply-filter="(newFilter) => filter = newFilter"></HomeEditor>
|
||||||
<h5 v-if="!editor">Restez sur l'image pour passer en mode éditeur</h5>
|
<Image class="home" v-if="imageSelectedId !== undefined && imageSelectedId !== -1" :id="imageSelectedId"
|
||||||
<h5 v-else>Restez sur l'image pour passer en mode selection</h5>
|
:images="images" :filter="filter">
|
||||||
<Image @mouseleave="clearTimer()" @mouseenter="startTimer()" class="home"
|
|
||||||
v-if="imageSelectedId !== undefined && imageSelectedId !== -1" :id="imageSelectedId" :images="images"
|
|
||||||
:filter="filter">
|
|
||||||
</Image>
|
</Image>
|
||||||
<a class="button" :href="images.get(imageSelectedId)?.data"
|
<a class="button" :href="images.get(imageSelectedId)?.data"
|
||||||
:download="images.get(imageSelectedId)?.name">Télécharger {{ images.get(imageSelectedId)?.name }}</a>
|
:download="images.get(imageSelectedId)?.name">Télécharger {{ images.get(imageSelectedId)?.name }}</a>
|
||||||
@@ -83,16 +79,13 @@ export default defineComponent({
|
|||||||
<h1 v-else>Aucune image trouvée</h1>
|
<h1 v-else>Aucune image trouvée</h1>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@import url("@/navi.css");
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
color: white;
|
color: white;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
h5 {
|
|
||||||
color: white;
|
|
||||||
margin: 1vw 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
@@ -106,37 +99,24 @@ html {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
gap: 4vh;
|
||||||
</style>
|
|
||||||
<style>
|
|
||||||
.home.imageContainer {
|
|
||||||
max-width: 60%;
|
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
max-height: 60vh;
|
|
||||||
margin: 0 0 2vw 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.home.imageContainer:hover {
|
.active {
|
||||||
animation: zoom-in-zoom-out 2s cubic-bezier(1, 0, 0.51, 1.01);
|
background-color: #646cff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active a {
|
||||||
|
color: rgb(205, 201, 194);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navi h3 {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.imageContainer) {
|
||||||
|
max-width: min(60vh, 60vw);
|
||||||
|
max-height: min(60vh, 60vw);
|
||||||
transform: none;
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes zoom-in-zoom-out {
|
|
||||||
0% {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
60% {
|
|
||||||
transform: scale(1.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
75% {
|
|
||||||
transform: scale(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { Filter, clone } from '@/filter'
|
import { Filter, clone, FilterType, RangeParameters, SelectParameters } from '@/filter'
|
||||||
import { api } from "@/http-api";
|
import { api } from "@/http-api";
|
||||||
|
import CustomSelector from './CustomSelector.vue';
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -15,73 +16,120 @@ export default defineComponent({
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
filterSelectId: 0,
|
filterSelectId: 0,
|
||||||
open: false,
|
|
||||||
filters: [] as Filter[],
|
filters: [] as Filter[],
|
||||||
error: false,
|
error: "",
|
||||||
}
|
filterApplied: false,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
api.getAlgorithmList().then((filters) => {
|
api.getAlgorithmList().then((filters) => {
|
||||||
this.filters = filters;
|
this.filters = filters;
|
||||||
|
// fill value attribute
|
||||||
|
for (const filter of this.filters) {
|
||||||
|
for (const arg of filter.parameters) {
|
||||||
|
if (arg.type === FilterType.range) {
|
||||||
|
const range = arg as RangeParameters;
|
||||||
|
range.value = range.min;
|
||||||
|
}
|
||||||
|
else if (arg.type === FilterType.select) {
|
||||||
|
const select = arg as SelectParameters;
|
||||||
|
select.value = select.options[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.error = this.areInputValid();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
applyFilter() {
|
applyFilter() {
|
||||||
if (this.filterSelectId === 0) {
|
if (this.error !== "")
|
||||||
this.$emit("applyFilter", undefined);
|
|
||||||
return;
|
return;
|
||||||
}
|
const filter = clone(this.filters[this.filterSelectId]);
|
||||||
const filter = clone(this.filters[this.filterSelectId - 1]);
|
|
||||||
for (const arg of filter.parameters) {
|
for (const arg of filter.parameters) {
|
||||||
if (arg.value === undefined) {
|
if (arg.value === undefined) {
|
||||||
this.error = true;
|
this.error = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.filterApplied = true;
|
||||||
this.$emit("applyFilter", filter);
|
this.$emit("applyFilter", filter);
|
||||||
|
},
|
||||||
|
areInputValid() {
|
||||||
|
for (const p of this.filters[this.filterSelectId].parameters) {
|
||||||
|
const input = document.getElementById(p.name) as HTMLInputElement;
|
||||||
|
if (input === null)
|
||||||
|
return "";
|
||||||
|
if (!input.checkValidity())
|
||||||
|
return "Paramètre(s) du filtre invalide(s)";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
watch: {
|
||||||
|
filterSelectId() {
|
||||||
|
this.error = this.areInputValid();
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
handler() {
|
||||||
|
this.error = this.areInputValid();
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: { CustomSelector }
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="editor">
|
<div class="editor">
|
||||||
<label>Sélectionner un filtre: </label>
|
<label>Sélectionner un filtre: </label>
|
||||||
<div class="custom-select" @blur="open = false">
|
<CustomSelector :list="filters.map((f) => { return f.name })" :selected="filterSelectId"
|
||||||
<div class="selected" :class="{ open: open }" @click="open = !open">
|
@update-selected="filterSelectId = $event" />
|
||||||
<span>{{ filterSelectId === 0 ? "Aucun filtre" : filters[filterSelectId - 1].name }}</span>
|
<!-- Filter parameters -->
|
||||||
|
<div v-if="filters.length !== 0" v-for="parameter in filters[filterSelectId].parameters" class="rangeInput">
|
||||||
|
<div v-if="parameter.type === FilterType.range">
|
||||||
|
<div class="labelInput">
|
||||||
|
<label>{{ parameter.name }}: </label>
|
||||||
|
<input type="number" :min="(parameter as RangeParameters).min" :max="(parameter as RangeParameters).max"
|
||||||
|
:step="(parameter as RangeParameters).step" v-model.number="parameter.value" :id="parameter.name" />
|
||||||
|
</div>
|
||||||
|
<input type="range" :min="(parameter as RangeParameters).min" :max="(parameter as RangeParameters).max"
|
||||||
|
:step="(parameter as RangeParameters).step" v-model.number="(parameter as RangeParameters).value" />
|
||||||
</div>
|
</div>
|
||||||
<div class="items" :class="{ selectHide: !open }">
|
<div v-else-if="parameter.type === FilterType.select" class="selectParam">
|
||||||
<div @click="open = false; filterSelectId = 0">
|
<label>{{ parameter.name }}: </label>
|
||||||
<span>Aucun filtre</span>
|
<CustomSelector :list="(parameter as SelectParameters).options"
|
||||||
</div>
|
:selected="(parameter as SelectParameters).options.findIndex((o) => o === (parameter as SelectParameters).value)"
|
||||||
<div v-for="(filter, index) in filters" @click="open = false; filterSelectId = index + 1">
|
@update-selected="parameter.value = (parameter as SelectParameters).options[$event]" />
|
||||||
<span>{{ filter.name }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="filters.length != 0 && filterSelectId !== 0" v-for="parameter in filters[filterSelectId - 1].parameters"
|
<button @mouseenter="error = areInputValid()" @mouseleave="error = areInputValid()" class="button"
|
||||||
class="rangeInput">
|
:class="{ errorClass: error !== '' }" @click="applyFilter()">
|
||||||
<label>{{ parameter.name }}: ({{ parameter.value }}) </label>
|
{{ error !== "" ? error : "Appliquer le filtre" }}</button>
|
||||||
<input type="range" :min="parameter.min" :max="parameter.max" :step="parameter.step"
|
<button v-if="filterApplied" class="button"
|
||||||
v-model.number="parameter.value" />
|
@click="$emit('applyFilter', undefined); filterApplied = false">Supprimer le filtre</button>
|
||||||
</div>
|
|
||||||
<button @mouseleave="error = false" class="button" :class="{ erreurClass: error }" @click="applyFilter()">
|
|
||||||
{{ error ? "Erreur" : "Appliquer le filtre" }}</button>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import url("@/customSelect.css");
|
|
||||||
|
|
||||||
label {
|
label {
|
||||||
color: white;
|
color: white;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.labelInput {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelInput input {
|
||||||
|
width: min-content;
|
||||||
|
height: 1em;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -90,8 +138,19 @@ label {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.erreurClass {
|
.errorClass {
|
||||||
background-color: red;
|
background-color: red;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:invalid {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectParam {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { ImageType } from '@/image'
|
import { ImageType } from '@/image'
|
||||||
|
import CustomSelector from './CustomSelector.vue';
|
||||||
</script>
|
</script>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
@@ -28,7 +28,6 @@ export default defineComponent({
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
sure: false,
|
sure: false,
|
||||||
open: false,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -52,25 +51,14 @@ export default defineComponent({
|
|||||||
<template>
|
<template>
|
||||||
<div class="selector">
|
<div class="selector">
|
||||||
<label>Sélectionner une image: </label>
|
<label>Sélectionner une image: </label>
|
||||||
<div class="custom-select" @blur="open = false">
|
<CustomSelector :list="Array.from(images.values()).map((i) => { return i.name })" :selected="imageSelectedId"
|
||||||
<div class="selected" :class="{ open: open }" @click="open = !open">
|
@update-selected="$emit('updateImageSelectedId', $event)" />
|
||||||
<span>{{ images.get(imageSelectedId)?.name }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="items" :class="{ selectHide: !open }">
|
|
||||||
<div v-for="[id, image] of images" :key="id"
|
|
||||||
@click="$emit('updateImageSelectedId', image.id); open = false;">
|
|
||||||
<span>{{ image.name }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="button" @mouseleave="sure = false" @click="confirmDelete()" :class="{ confirm: sure }">
|
<button class="button" @mouseleave="sure = false" @click="confirmDelete()" :class="{ confirm: sure }">
|
||||||
{{ sure ? "Confirmer" : "Supprimer" }}</button>
|
{{ sure ? "Confirmer" : "Supprimer" }}</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import url(@/customSelect.css);
|
|
||||||
|
|
||||||
.selector {
|
.selector {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@@ -73,9 +73,7 @@ export default defineComponent({
|
|||||||
<style>
|
<style>
|
||||||
.imageContainer img {
|
.imageContainer img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100vh;
|
max-height: 100%;
|
||||||
height: auto;
|
|
||||||
width: auto;
|
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,248 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="preview">
|
||||||
|
<div v-if="started" class="loadingbar">
|
||||||
|
<div class="loadedbar" :style="{ width: progress + '%' }">
|
||||||
|
<span>ETA: {{ eta }}s</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<img :src="generatedImage" />
|
||||||
|
</div>
|
||||||
|
<div class="dl" v-if="generatedImage != ''">
|
||||||
|
<a :href="generatedImage" download="generated.png" class="button">Download</a>
|
||||||
|
<button class="button" @click="sendImage()" :class="{ sent: sent }">{{ sent ? "Envoyé" : "Upload to server"
|
||||||
|
}}</button>
|
||||||
|
</div>
|
||||||
|
<div class="form">
|
||||||
|
<label for="promptImage">Prompt Image</label>
|
||||||
|
<textarea v-model="promptImage" />
|
||||||
|
<label for="negativePromptImage">Negative Prompt Image</label>
|
||||||
|
<textarea v-model="negativePromptImage" />
|
||||||
|
<div class="labelInput">
|
||||||
|
<label for="height">Height</label>
|
||||||
|
<input type="number" v-model.number="height" />
|
||||||
|
</div>
|
||||||
|
<input type="range" :min="minmaxHeight.min" :max="minmaxHeight.max" v-model.number="height" />
|
||||||
|
<div class="labelInput">
|
||||||
|
<label for="width">Width</label>
|
||||||
|
<input type="number" v-model.number="width" />
|
||||||
|
</div>
|
||||||
|
<input type="range" :min="minmaxWidth.min" :max="minmaxWidth.max" v-model.number="width" />
|
||||||
|
<div class="labelInput">
|
||||||
|
<label for="steps">Steps</label>
|
||||||
|
<input type="number" v-model.number="steps" />
|
||||||
|
</div>
|
||||||
|
<input type="range" :min="minmaxSteps.min" :max="minmaxSteps.max" v-model.number="steps" />
|
||||||
|
<div class="labelInput">
|
||||||
|
<label for="cfgScale">Cfg Scale</label>
|
||||||
|
<input type="number" v-model.number="cfgScale" />
|
||||||
|
</div>
|
||||||
|
<input type="range" :min="minmaxCfgScale.min" :max="minmaxCfgScale.max" v-model.number="cfgScale" />
|
||||||
|
<button :disabled="started" class="button" @click="generate">{{ started ? "Generation en cours" :
|
||||||
|
"Generate" }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { api } from '@/http-api';
|
||||||
|
</script>
|
||||||
|
<script lang="ts">
|
||||||
|
|
||||||
|
const minmaxHeight = {
|
||||||
|
min: 400,
|
||||||
|
max: 512
|
||||||
|
}
|
||||||
|
|
||||||
|
const minmaxWidth = {
|
||||||
|
min: 400,
|
||||||
|
max: 512
|
||||||
|
}
|
||||||
|
|
||||||
|
const minmaxSteps = {
|
||||||
|
min: 10,
|
||||||
|
max: 40
|
||||||
|
}
|
||||||
|
|
||||||
|
const minmaxCfgScale = {
|
||||||
|
min: 0,
|
||||||
|
max: 30
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
emits: ['updateImageList'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
promptImage: '',
|
||||||
|
negativePromptImage: '',
|
||||||
|
height: 512,
|
||||||
|
width: 512,
|
||||||
|
steps: 20,
|
||||||
|
cfgScale: 7,
|
||||||
|
generatedImage: '',
|
||||||
|
started: false,
|
||||||
|
progress: 0,
|
||||||
|
eta: 0,
|
||||||
|
sent: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async generate() {
|
||||||
|
api.generateImage(this.promptImage, this.negativePromptImage, this.height, this.width, this.steps, this.cfgScale).then((data) => {
|
||||||
|
this.generatedImage = "data:image/png;base64," + data.images[0];
|
||||||
|
this.started = false;
|
||||||
|
});
|
||||||
|
this.sent = false;
|
||||||
|
this.generatedImage = '';
|
||||||
|
this.started = true;
|
||||||
|
},
|
||||||
|
urltoFile(url: string, filename: string, mimeType: string) {
|
||||||
|
return (fetch(url)
|
||||||
|
.then(function (res) { return res.arrayBuffer(); })
|
||||||
|
.then(function (buf) { return new File([buf], filename, { type: mimeType }); })
|
||||||
|
);
|
||||||
|
},
|
||||||
|
async sendImage() {
|
||||||
|
if (!this.sent) {
|
||||||
|
this.sent = true;
|
||||||
|
const file = await this.urltoFile(this.generatedImage, "generated" + Date.now() + ".png", "image/png");
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
api.createImage(formData).then((data) => {
|
||||||
|
console.log(data);
|
||||||
|
this.$emit('updateImageList');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
height() {
|
||||||
|
if (this.height < minmaxHeight.min)
|
||||||
|
this.height = minmaxHeight.min;
|
||||||
|
else if (this.height > minmaxHeight.max)
|
||||||
|
this.height = minmaxHeight.max;
|
||||||
|
},
|
||||||
|
width() {
|
||||||
|
if (this.width < minmaxWidth.min)
|
||||||
|
this.width = minmaxWidth.min;
|
||||||
|
else if (this.width > minmaxWidth.max)
|
||||||
|
this.width = minmaxWidth.max;
|
||||||
|
},
|
||||||
|
steps() {
|
||||||
|
if (this.steps < minmaxSteps.min)
|
||||||
|
this.steps = minmaxSteps.min;
|
||||||
|
else if (this.steps > minmaxSteps.max)
|
||||||
|
this.steps = minmaxSteps.max;
|
||||||
|
},
|
||||||
|
cfgScale() {
|
||||||
|
if (this.cfgScale < minmaxCfgScale.min)
|
||||||
|
this.cfgScale = minmaxCfgScale.min;
|
||||||
|
else if (this.cfgScale > minmaxCfgScale.max)
|
||||||
|
this.cfgScale = minmaxCfgScale.max;
|
||||||
|
},
|
||||||
|
started() {
|
||||||
|
if (this.started) {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
api.progress().then((data) => {
|
||||||
|
this.progress = data.progress * 100;
|
||||||
|
this.eta = Math.floor(data.eta_relative);
|
||||||
|
if (data.current_image !== null) {
|
||||||
|
this.generatedImage = "data:image/png;base64," + data.current_image;
|
||||||
|
}
|
||||||
|
if (data.state.job_count === 0) {
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
if (!this.started) {
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sent {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form textarea {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 1vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form input {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 1vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.button {
|
||||||
|
text-decoration: none;
|
||||||
|
height: 1.5em;
|
||||||
|
width: min-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dl {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px;
|
||||||
|
gap: 1vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelInput {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.labelInput input {
|
||||||
|
width: 37px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadingbar {
|
||||||
|
width: 100%;
|
||||||
|
height: 25px;
|
||||||
|
background-color: #c9c9c9;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadedbar {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #4caf50;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: row;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -13,7 +13,7 @@ interface InputFileEvent extends Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
emits: ['updateImageList', 'delete'],
|
emits: ['updateImageList'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
file: null as FileList | null,
|
file: null as FileList | null,
|
||||||
@@ -27,8 +27,15 @@ export default defineComponent({
|
|||||||
images: {
|
images: {
|
||||||
type: Map<number, ImageType>,
|
type: Map<number, ImageType>,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
fileDefault: {
|
||||||
|
type: FileList,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.file = this.fileDefault;
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async submitImage() {
|
async submitImage() {
|
||||||
if (this.classSendButton === "sent") {
|
if (this.classSendButton === "sent") {
|
||||||
|
|||||||
+57
-12
@@ -1,22 +1,48 @@
|
|||||||
export class Parameters {
|
export enum FilterType {
|
||||||
|
"range" = "range",
|
||||||
|
"select" = "select",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export abstract class Parameters {
|
||||||
name: string
|
name: string
|
||||||
displayName: string
|
displayName: string
|
||||||
min: number
|
type: FilterType
|
||||||
max: number
|
value: any
|
||||||
step: number
|
|
||||||
value: number | undefined
|
|
||||||
|
|
||||||
constructor(name: string, displayName: string, min: number, max: number, step: number = 1, value: number | undefined = undefined) {
|
constructor(name: string, displayName: string, type: FilterType) {
|
||||||
this.name = name
|
this.name = name
|
||||||
this.displayName = displayName
|
this.displayName = displayName
|
||||||
this.min = min
|
this.type = type
|
||||||
this.max = max
|
|
||||||
this.step = step
|
|
||||||
this.value = value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class RangeParameters extends Parameters {
|
||||||
|
min: number
|
||||||
|
max: number
|
||||||
|
step: number
|
||||||
|
value: number
|
||||||
|
constructor(name: string, displayName: string, min: number, max: number, step: number, value: number = min) {
|
||||||
|
super(name, displayName, FilterType.range)
|
||||||
|
this.value = value;
|
||||||
|
this.min = min
|
||||||
|
this.max = max
|
||||||
|
this.step = step
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SelectParameters extends Parameters {
|
||||||
|
options: string[]
|
||||||
|
value: string
|
||||||
|
constructor(name: string, displayName: string, options: string[], value = options[0]) {
|
||||||
|
super(name, displayName, FilterType.select)
|
||||||
|
this.value = value;
|
||||||
|
this.options = options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export class Filter {
|
export class Filter {
|
||||||
name: string
|
name: string
|
||||||
path: string
|
path: string
|
||||||
@@ -31,9 +57,28 @@ export class Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getParameters(f: Filter): string {
|
export function getParameters(f: Filter): string {
|
||||||
return '&' + f.parameters.map((param) => param.name + "=" + param.value).join("&");
|
return '&' + f.parameters.map((param) => {
|
||||||
|
switch (param.type) {
|
||||||
|
case FilterType.range:
|
||||||
|
const paramCastRange = param as RangeParameters
|
||||||
|
return paramCastRange.name + "=" + paramCastRange.value
|
||||||
|
case FilterType.select:
|
||||||
|
const paramCastSelect = param as SelectParameters
|
||||||
|
return paramCastSelect.name + "=" + paramCastSelect.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).join("&")
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clone(f: Filter): Filter {
|
export function clone(f: Filter): Filter {
|
||||||
return new Filter(f.name, f.path, f.parameters.map((arg) => new Parameters(arg.name, arg.displayName, arg.min, arg.max, arg.step, arg.value)))
|
return new Filter(f.name, f.path, f.parameters.map((param) => {
|
||||||
|
switch (param.type) {
|
||||||
|
case FilterType.range:
|
||||||
|
const paramCastRange = param as RangeParameters
|
||||||
|
return new RangeParameters(param.name, param.displayName, paramCastRange.min, paramCastRange.max, paramCastRange.step, paramCastRange.value)
|
||||||
|
case FilterType.select:
|
||||||
|
const paramCastSelect = param as SelectParameters
|
||||||
|
return new SelectParameters(param.name, param.displayName, paramCastSelect.options, paramCastSelect.value)
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,44 @@ import axios, { AxiosResponse, AxiosError, HttpStatusCode } from 'axios';
|
|||||||
import { ImageType } from '@/image';
|
import { ImageType } from '@/image';
|
||||||
import { Filter, getParameters } from './filter';
|
import { Filter, getParameters } from './filter';
|
||||||
|
|
||||||
|
interface stateInterface {
|
||||||
|
skipped: boolean;
|
||||||
|
interrupted: boolean;
|
||||||
|
job: string;
|
||||||
|
job_count: number;
|
||||||
|
job_timestamp: string;
|
||||||
|
job_no: number;
|
||||||
|
sampling_step: number;
|
||||||
|
sampling_steps: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface progressInterface {
|
||||||
|
progress: number;
|
||||||
|
eta_relative: number;
|
||||||
|
state: stateInterface;
|
||||||
|
current_image: string | null;
|
||||||
|
text_info: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface generateImageInterface {
|
||||||
|
images: string[];
|
||||||
|
parameters: {};
|
||||||
|
info: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const cancelRequest = axios.CancelToken.source();
|
||||||
|
|
||||||
const instance = axios.create({
|
const instance = axios.create({
|
||||||
baseURL: "/",
|
baseURL: "/",
|
||||||
timeout: 15000,
|
timeout: 0,
|
||||||
|
cancelToken: cancelRequest.token
|
||||||
|
});
|
||||||
|
|
||||||
|
const stableDiffInstance = axios.create({
|
||||||
|
baseURL: "http://stable.nessar.fr/",
|
||||||
|
timeout: 0,
|
||||||
|
cancelToken: cancelRequest.token
|
||||||
});
|
});
|
||||||
|
|
||||||
const responseBody = (response: AxiosResponse) => response.data;
|
const responseBody = (response: AxiosResponse) => response.data;
|
||||||
@@ -14,7 +49,12 @@ const requests = {
|
|||||||
get: (url: string, param: {}) => instance.get(url, param).then(responseBody),
|
get: (url: string, param: {}) => instance.get(url, param).then(responseBody),
|
||||||
post: (url: string, body: {}) => instance.post(url, body, { headers: { "Content-Type": "multipart/form-data" }, }).then(responseStatus),
|
post: (url: string, body: {}) => instance.post(url, body, { headers: { "Content-Type": "multipart/form-data" }, }).then(responseStatus),
|
||||||
put: (url: string, body: {}) => instance.put(url, body).then(responseBody),
|
put: (url: string, body: {}) => instance.put(url, body).then(responseBody),
|
||||||
delete: (url: string) => instance.delete(url).then(responseBody)
|
delete: (url: string) => instance.delete(url).then(responseBody),
|
||||||
|
};
|
||||||
|
|
||||||
|
const stableDiffRequests = {
|
||||||
|
get: (url: string, param: {}) => stableDiffInstance.get(url, param).then(responseBody),
|
||||||
|
post: (url: string, body: {}) => stableDiffInstance.post(url, body, { headers: { "Content-Type": "application/json" }, }).then(responseBody),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
@@ -23,4 +63,18 @@ export const api = {
|
|||||||
getImage: (id: number, filter: Filter | undefined): Promise<Blob> => requests.get(`images/${id}${filter !== undefined ? "?algorithm=" + filter.path + getParameters(filter) : ""}`, { responseType: "blob" }),
|
getImage: (id: number, filter: Filter | undefined): Promise<Blob> => requests.get(`images/${id}${filter !== undefined ? "?algorithm=" + filter.path + getParameters(filter) : ""}`, { responseType: "blob" }),
|
||||||
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}`),
|
||||||
|
generateImage: (prompt: string, negativePrompt: string, height: number, width: number, steps: number, cfgScale: number): Promise<generateImageInterface> => stableDiffRequests.post('sdapi/v1/txt2img', {
|
||||||
|
"prompt": prompt,
|
||||||
|
"negative_prompt": negativePrompt,
|
||||||
|
"height": height,
|
||||||
|
"width": width,
|
||||||
|
"steps": steps,
|
||||||
|
"cfg_scale": cfgScale,
|
||||||
|
"do_not_save_samples": true,
|
||||||
|
"do_not_save_grid": true,
|
||||||
|
"sampler_index": "Euler a",
|
||||||
|
"send_images": true,
|
||||||
|
"save_images": false,
|
||||||
|
}),
|
||||||
|
progress: (): Promise<progressInterface> => stableDiffRequests.get('sdapi/v1/progress', {}),
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
.navi {
|
||||||
|
/* reduce size of children */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
pointer-events: none;
|
||||||
|
gap: 3vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navi ul>li {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navi h3,
|
||||||
|
.navi a {
|
||||||
|
min-width: 90px;
|
||||||
|
padding: 5px 15px;
|
||||||
|
border-radius: 60px;
|
||||||
|
color: rgb(172, 165, 154);
|
||||||
|
text-decoration: none;
|
||||||
|
font-family: "Aeonik", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navi nav {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-left: 20px;
|
||||||
|
height: 60px;
|
||||||
|
padding-right: 20px;
|
||||||
|
border-radius: 60px;
|
||||||
|
background-color: rgb(24, 26, 27);
|
||||||
|
margin-right: 20px;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.navi ul {
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
@@ -18,6 +18,11 @@ const router = createRouter({
|
|||||||
name: "upload",
|
name: "upload",
|
||||||
component: () => import('../components/Upload.vue')
|
component: () => import('../components/Upload.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/generateImages',
|
||||||
|
name: "generate",
|
||||||
|
component: () => import('../components/StableDiff.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
name: 'not-found',
|
name: 'not-found',
|
||||||
|
|||||||
Reference in new issue
Block a user