diff --git a/backend/src/main/java/pdl/backend/ImageController.java b/backend/src/main/java/pdl/backend/ImageController.java index 2695032..af937cb 100644 --- a/backend/src/main/java/pdl/backend/ImageController.java +++ b/backend/src/main/java/pdl/backend/ImageController.java @@ -1,15 +1,24 @@ package pdl.backend; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.util.List; import java.util.Optional; +import java.util.Map; + +import javax.imageio.ImageIO; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import boofcv.io.image.ConvertBufferedImage; +import boofcv.struct.image.GrayU8; +import boofcv.struct.image.Planar; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpStatus; @@ -24,6 +33,9 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import java.awt.image.BufferedImage; @RestController public class ImageController { @@ -37,43 +49,34 @@ public class ImageController { this.imageDao = imageDao; } - @GetMapping(value = "/images/{id}", produces = MediaType.IMAGE_JPEG_VALUE) - public ResponseEntity getImage(@PathVariable("id") long id) { - - Optional image = imageDao.retrieve(id); - - if (image.isPresent()) { - InputStream inputStream = new ByteArrayInputStream(image.get().getData()); - return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).body(new InputStreamResource(inputStream)); - } - return new ResponseEntity<>("Image id=" + id + " not found.", HttpStatus.NOT_FOUND); - } - @DeleteMapping(value = "/images/{id}") public ResponseEntity deleteImage(@PathVariable("id") long id) { - - Optional image = imageDao.retrieve(id); - - if (image.isPresent()) { - imageDao.delete(image.get()); - return new ResponseEntity<>("Image id=" + id + " deleted.", HttpStatus.OK); - } - return new ResponseEntity<>("Image id=" + id + " not found.", HttpStatus.NOT_FOUND); + var img = imageDao.retrieve(id); + if (img.isEmpty()) + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + imageDao.delete(img.get()); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } - @PostMapping(value = "/images") + @PostMapping(value = "/images", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity addImage(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) { - String contentType = file.getContentType(); - if (contentType != null && !contentType.equals(MediaType.IMAGE_JPEG.toString())) { - return new ResponseEntity<>("Only JPEG/PNG file format supported", HttpStatus.UNSUPPORTED_MEDIA_TYPE); - } + if (contentType == null + || (!contentType.equals(MediaType.IMAGE_JPEG_VALUE) && !contentType.equals(MediaType.IMAGE_PNG_VALUE))) + return new ResponseEntity<>(HttpStatus.UNSUPPORTED_MEDIA_TYPE); try { - imageDao.create(new Image(file.getOriginalFilename(), file.getBytes())); + Image newImg = new Image(file.getOriginalFilename(), file.getBytes()); + imageDao.create(newImg); + URI newURI = ServletUriComponentsBuilder.fromCurrentRequest().path("/" + newImg.getId()).build().toUri(); + return ResponseEntity.created(newURI).build(); } catch (IOException e) { - return new ResponseEntity<>("Failure to read file", HttpStatus.NO_CONTENT); + return new ResponseEntity<>(HttpStatus.NOT_MODIFIED); } - return new ResponseEntity<>("Image uploaded", HttpStatus.CREATED); + } + + @DeleteMapping(value = "/images") + public ResponseEntity deleteImagesList() { + return new ResponseEntity<>(HttpStatus.METHOD_NOT_ALLOWED); } @GetMapping(value = "/images", produces = "application/json") @@ -92,4 +95,110 @@ public class ImageController { return nodes; } -} + @GetMapping(value = "/images/{id}", produces = { MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE }) + public ResponseEntity getImage(@PathVariable("id") long id, + @RequestParam(required = false) Map parameters) { + + // Récupère l'image correspondante à l'ID fourni + Optional optImage = imageDao.retrieve(id); + if (!optImage.isPresent()) + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + Image image = optImage.get(); + // Si aucun paramètre n'est fourni, renvoie l'image brute + if (parameters.isEmpty()) { + InputStream inputStream = new ByteArrayInputStream(image.getData()); + return ResponseEntity.ok().contentType(image.getMediaType()) + .body(new InputStreamResource(inputStream)); + } + // Vérifie la présence du paramètre "algorithm" + if (!parameters.containsKey("algorithm")) + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + + String algo = parameters.get("algorithm"); + BufferedImage bufImg; + try { + bufImg = ImageIO.read(new ByteArrayInputStream(image.getData())); + } catch (IOException e) { + System.out.println("Error while reading image"); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + Planar input = ConvertBufferedImage.convertFromPlanar(bufImg, null, true, GrayU8.class); + + // applique l'algo a l'image en fonction des parametres et verifie la validite + // des parametres + switch (algo) { + case "changeLuminosity": + if (!parameters.containsKey("delta") || parameters.size() != 2) + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + try { + int i = Integer.parseInt(parameters.get("delta")); + ImageProcessing.changeLuminosity(input, i); + } catch (NumberFormatException e) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + break; + case "histogram": + if (parameters.size() != 1) + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + ImageProcessing.histogram(input); + break; + case "colorFilter": + if (!parameters.containsKey("hue") || parameters.size() != 2) + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + try { + int i = Integer.parseInt(parameters.get("hue")); + ImageProcessing.colorFilter(i, input); + // hue entre 0 et 360? + } catch (NumberFormatException e) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + break; + case "meanFilter": + if (!parameters.containsKey("size") || parameters.size() != 2) + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + try { + int i = Integer.parseInt(parameters.get("size")); + if (i % 2 == 0) + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + Planar clone = input.clone(); + ImageProcessing.meanFilter(clone, input, i); + } catch (NumberFormatException e) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + break; + case "gaussienFilter": + if (!parameters.containsKey("size") || parameters.size() != 2) + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + try { + int i = Integer.parseInt(parameters.get("size")); + if (i % 2 == 0) + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + Planar clone = input.clone(); + ImageProcessing.gaussienFilter(clone, input, i); + } catch (NumberFormatException e) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + break; + case "contours": + if (parameters.size() != 1) + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + Planar clone = input.clone(); + ImageProcessing.gradientImageSobel(clone, input); + break; + default: + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + // transforme l'image en quelque chose que le navigateur puisse lire + bufImg = ConvertBufferedImage.convertTo_U8(input, null, true); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + ImageIO.write(bufImg, image.getMediaType().getSubtype(), baos); + } catch (IOException e) { + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + byte[] imageData = baos.toByteArray(); + InputStream inputStream = new ByteArrayInputStream(imageData); + return ResponseEntity.ok().contentType(image.getMediaType()).body(new InputStreamResource(inputStream)); + } +} \ No newline at end of file diff --git a/backend/src/main/java/pdl/backend/ImageProcessing.java b/backend/src/main/java/pdl/backend/ImageProcessing.java index c396778..4d1fc3f 100644 --- a/backend/src/main/java/pdl/backend/ImageProcessing.java +++ b/backend/src/main/java/pdl/backend/ImageProcessing.java @@ -314,6 +314,36 @@ public class ImageProcessing { } + /** + * TODO + * Calcule le noyau gaussien de taille demandée + * + * @param size taille du noyau gaussien + * @return noyau gaussien + */ + public static int[][] createGaussienKernel(int size) { + // Vérification que la taille est impaire + if (size % 2 == 0 || size < 0) { + throw new IllegalArgumentException("La taille doit être impaire et positive"); + } + double[][] kernel = new double[size][size]; + double sigma = 4 / 3; + double sum = 0.0; // For accumulating the kernel values + for (int x = 0; x < size; ++x) + for (int y = 0; y < size; ++y) { + kernel[x][y] = 1 / (2 * Math.PI * sigma * sigma) * Math.exp(-(x * x + y * y) / (2 * sigma * sigma)); + + } + + // Normalize the kernel + int[][] kernelk = new int[size][size]; + for (int x = 0; x < size; ++x) + for (int y = 0; y < size; ++y) + kernelk[x][y] = (int) (kernel[x][y] / sum); + + return kernelk; + } + /** * * Effectue une convolution sur une image avec le noyau @@ -324,8 +354,9 @@ public class ImageProcessing { * @param kernel Le noyau de convolution à utiliser. */ public static void gaussienFilter(Planar input, Planar output, - int[][] kernel) { - int size = kernel.length; + int size) { + int[][] kernel = { { 1, 2, 3, 2, 1 }, { 2, 6, 8, 6, 2 }, { 3, 8, 10, 8, 3 }, { 2, 6, 8, 6, 2 }, + { 1, 2, 3, 2, 1 } }; int half = size / 2; int totalCoefs = 0; // Calcul du nombre total de coefficients dans le noyau diff --git a/backend/src/test/java/pdl/backend/ImageControllerTests.java b/backend/src/test/java/pdl/backend/ImageControllerTests.java index da44224..cce4d95 100644 --- a/backend/src/test/java/pdl/backend/ImageControllerTests.java +++ b/backend/src/test/java/pdl/backend/ImageControllerTests.java @@ -86,7 +86,7 @@ public class ImageControllerTests { @Test @Order(6) public void deleteImageShouldReturnSuccess() throws Exception { - this.mockMvc.perform(delete("/images/0")).andDo(print()).andExpect(status().isOk()); + this.mockMvc.perform(delete("/images/0")).andDo(print()).andExpect(status().isNoContent()); } @Test