code factorized, gaussienFilter done, borders added, imageController updated
This commit is contained in:
1 parent
522bbc73cf
commit
29ab434586
2 files changed
+158
-75
No files matched your search
@@ -16,6 +16,7 @@ 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;
|
||||||
|
|
||||||
@@ -135,6 +136,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é
|
||||||
*
|
*
|
||||||
@@ -210,8 +228,9 @@ public class ImageController {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "meanFilter":
|
case "meanFilter":
|
||||||
if (!parameters.containsKey("size") || parameters.size() != 2)
|
if (!parameters.containsKey("size") || !parameters.containsKey("borderType") || parameters.size() != 3)
|
||||||
return JSONError("Algorithm 'meanFilter' requires one parameter 'size'(positive odd integer)",
|
return JSONError(
|
||||||
|
"Algorithm 'meanFilter' requires two parameters: 'size'(positive odd integer) and 'borderType'('SKIP', 'EXTENDED', 'WRAP', 'REFLECT', 'ZERO' or 'NORMALIZED')",
|
||||||
HttpStatus.BAD_REQUEST);
|
HttpStatus.BAD_REQUEST);
|
||||||
try {
|
try {
|
||||||
int i = Integer.parseInt(parameters.get("size"));
|
int i = Integer.parseInt(parameters.get("size"));
|
||||||
@@ -219,24 +238,40 @@ public class ImageController {
|
|||||||
return JSONError("Parameter 'size' should be a positive odd integer",
|
return JSONError("Parameter 'size' should be a positive odd integer",
|
||||||
HttpStatus.BAD_REQUEST);
|
HttpStatus.BAD_REQUEST);
|
||||||
Planar<GrayU8> clone = input.clone();
|
Planar<GrayU8> clone = input.clone();
|
||||||
ImageProcessing.meanFilter(clone, input, i);
|
BorderType borderType = getBorderType(parameters.get("borderType"));
|
||||||
|
if (borderType == null) {
|
||||||
|
return JSONError(
|
||||||
|
"Parameter 'borderType' should be 'SKIP', 'EXTENDED', 'WRAP', 'REFLECT', 'ZERO' or 'NORMALIZED'",
|
||||||
|
HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
ImageProcessing.meanFilter(clone, input, i, borderType);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
return JSONError("Parameter 'size' should be a positive odd integer",
|
return JSONError("Parameter 'size' should be a positive odd integer",
|
||||||
HttpStatus.BAD_REQUEST);
|
HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "gaussienFilter":
|
case "gaussienFilter":
|
||||||
if (/* !parameters.containsKey("size") || */ parameters.size() != 1)
|
if (!parameters.containsKey("size") || !parameters.containsKey("sigma")
|
||||||
return JSONError("Algorithm 'gaussienFilter' requires no parameters", HttpStatus.BAD_REQUEST);
|
|| !parameters.containsKey("borderType") || parameters.size() != 5)
|
||||||
|
return JSONError(
|
||||||
|
"Algorithm 'gaussienFilter' requires 3 parameters: 'size'(positive odd integer), 'sigma'(positive number) and 'borderType'('SKIP', 'EXTENDED', 'WRAP', 'REFLECT', 'ZERO' or 'NORMALIZED')",
|
||||||
|
HttpStatus.BAD_REQUEST);
|
||||||
try {
|
try {
|
||||||
// int i = Integer.parseInt(parameters.get("size"));
|
int i = Integer.parseInt(parameters.get("size"));
|
||||||
// if (i % 2 == 0)
|
if (i % 2 == 0 || i < 0)
|
||||||
// return JSONError("Parameter 'size' should be a positive odd integer",
|
return JSONError("Parameter 'size' should be a positive odd integer",
|
||||||
// HttpStatus.BAD_REQUEST);
|
HttpStatus.BAD_REQUEST);
|
||||||
|
double sigma = Double.parseDouble(parameters.get("sigma"));
|
||||||
Planar<GrayU8> clone = input.clone();
|
Planar<GrayU8> clone = input.clone();
|
||||||
ImageProcessing.gaussienFilter(clone, input, 5);
|
BorderType borderType = getBorderType(parameters.get("borderType"));
|
||||||
|
if (borderType == null) {
|
||||||
|
return JSONError(
|
||||||
|
"Parameter 'borderType' should be 'SKIP', 'EXTENDED', 'WRAP', 'REFLECT', 'ZERO' or 'NORMALIZED'",
|
||||||
|
HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
ImageProcessing.gaussienFilter(clone, input, i, sigma, borderType);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
return JSONError("Parameter 'size' should be a positive odd integer",
|
return JSONError("Parameters 'size' and 'sigma' should be a positive numbers",
|
||||||
HttpStatus.BAD_REQUEST);
|
HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package pdl.backend;
|
package pdl.backend;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -279,74 +280,77 @@ public class ImageProcessing {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 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, Planar<GrayU8> output, 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, output, 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 output L'image de sortie
|
||||||
|
* @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, Planar<GrayU8> output,
|
||||||
// Vérification que la taille est impaire
|
int size, double sigma, BorderType borderType) {
|
||||||
if (size % 2 == 0 || size < 0) {
|
if (size % 2 == 0 || size < 0 || sigma <= 0) {
|
||||||
throw new IllegalArgumentException("La taille doit être impaire et positive");
|
throw new IllegalArgumentException(
|
||||||
|
"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, output, kernelInt, borderType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -354,15 +358,15 @@ 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 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, Planar<GrayU8> output,
|
||||||
int size) {
|
int[][] kernel, BorderType borderType) {
|
||||||
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++) {
|
||||||
@@ -374,10 +378,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(output, x, y, getRGBValue(input, x, y));
|
||||||
|
} else if (borderType == BorderType.ZERO) {
|
||||||
|
setRGBValue(output, 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] += input.getBand(k).get(tmpi, tmpj)
|
||||||
|
* kernel[tmpi][tmpj];
|
||||||
|
}
|
||||||
|
coefsUsed += kernel[tmpi][tmpj];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int k = 0; k < totalValue.length; k++) {
|
||||||
|
totalValue[k] /= coefsUsed;
|
||||||
|
}
|
||||||
|
setRGBValue(input, x, y, totalValue);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int currentJ = 0;
|
int currentJ = 0;
|
||||||
|
|||||||
Reference in new issue
Block a user