Commit 24ab992d authored by BORNON Théophile's avatar BORNON Théophile

merge

parents c6ed9768 c43bd2bc
...@@ -5,10 +5,6 @@ VisualStudioVersion = 15.0.27004.2010 ...@@ -5,10 +5,6 @@ VisualStudioVersion = 15.0.27004.2010
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessing", "S04_Projet\ImageProcessing.csproj", "{229D3602-654B-498B-9478-5CC3982C358C}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessing", "S04_Projet\ImageProcessing.csproj", "{229D3602-654B-498B-9478-5CC3982C358C}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessing_Benchmark", "ImageProcessing_Benchmark\ImageProcessing_Benchmark.csproj", "{54D04E0E-009D-4928-BEF8-B2FF464536CC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessing_Test", "ImageProcessing_Test\ImageProcessing_Test.csproj", "{F0906169-0946-4552-A06C-1D57C52B1C10}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
...@@ -19,14 +15,6 @@ Global ...@@ -19,14 +15,6 @@ Global
{229D3602-654B-498B-9478-5CC3982C358C}.Debug|Any CPU.Build.0 = Debug|Any CPU {229D3602-654B-498B-9478-5CC3982C358C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{229D3602-654B-498B-9478-5CC3982C358C}.Release|Any CPU.ActiveCfg = Release|Any CPU {229D3602-654B-498B-9478-5CC3982C358C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{229D3602-654B-498B-9478-5CC3982C358C}.Release|Any CPU.Build.0 = Release|Any CPU {229D3602-654B-498B-9478-5CC3982C358C}.Release|Any CPU.Build.0 = Release|Any CPU
{54D04E0E-009D-4928-BEF8-B2FF464536CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54D04E0E-009D-4928-BEF8-B2FF464536CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54D04E0E-009D-4928-BEF8-B2FF464536CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54D04E0E-009D-4928-BEF8-B2FF464536CC}.Release|Any CPU.Build.0 = Release|Any CPU
{F0906169-0946-4552-A06C-1D57C52B1C10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F0906169-0946-4552-A06C-1D57C52B1C10}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F0906169-0946-4552-A06C-1D57C52B1C10}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F0906169-0946-4552-A06C-1D57C52B1C10}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
......
...@@ -13,31 +13,47 @@ namespace S04_Projet ...@@ -13,31 +13,47 @@ namespace S04_Projet
/// </summary> /// </summary>
public enum Operation public enum Operation
{ {
Rotate90 = 0, Enlarge = 0,
Shrink,
Rotate90,
Rotate180, Rotate180,
Rotate270, Rotate270,
GrayScaleLinear, GrayScaleLinear,
GrayScaleLuminosity, GrayScaleLuminosity,
Shrink, Superposition,
SwitchMultiThreading, Filter,
//CreateImage,
Histogram,
Dithering,
Debug,
Multithreading,
Save Save
} }
private static string[] menuItems = new string[] private static string[] menuItems = new string[]
{ {
"Rotation à 90°", "Agrandir l'image",
"Rotation à 180°", "Rétrécir l'image",
"Rotation à 270°", "Rotation à 90°",
"Passage à une image en nuances de gris (linéairement)", "Rotation à 180°",
"Passage à une image en nuances de gris (luminosité)", "Rotation à 270°",
"Rétrécissement de l'image", "Passage à une image en nuances de gris (linéairement)",
"Désactiver le multithreading", "Passage à une image en nuances de gris (luminosité)",
"Sauvegarder" "Superposer avec une autre image",
"Ajouter un filtre de convolution",
"Générer les histogrammes de l'image",
"Floyd-Steinberg dithering",
"Activer le mode debug",
"Activer le multithreading",
"Sauvegarder"
}; };
public static long elapsedTime; public static long elapsedTime;
public static string lastOperationMessage; public static string lastOperationMessage;
public static string fileInfos; public static string fileInfos;
private static Stopwatch sw = new Stopwatch();
private static Menu mainMenu = new Menu(menuItems);
private static Menu filterMenu = new Menu(Filter.Noms);
/// <summary> /// <summary>
/// Charge l'image /// Charge l'image
...@@ -48,6 +64,7 @@ namespace S04_Projet ...@@ -48,6 +64,7 @@ namespace S04_Projet
string path; string path;
bool validPath = false; bool validPath = false;
byte[] file = new byte[0]; byte[] file = new byte[0];
do do
{ {
Console.WriteLine("Veuillez saisir le nom du fichier à traiter :"); Console.WriteLine("Veuillez saisir le nom du fichier à traiter :");
...@@ -66,15 +83,9 @@ namespace S04_Projet ...@@ -66,15 +83,9 @@ namespace S04_Projet
} while (!validPath); } while (!validPath);
Console.Clear(); Console.Clear();
Thread waitingThread = new Thread(new ThreadStart(WaitingScreen));
Stopwatch sw = new Stopwatch();
sw.Start(); sw.Start();
waitingThread.Start();
MyImage image = new MyImage(path); MyImage image = new MyImage(path);
waitingThread.Abort();
sw.Stop(); sw.Stop();
Console.Clear(); Console.Clear();
...@@ -99,145 +110,345 @@ namespace S04_Projet ...@@ -99,145 +110,345 @@ namespace S04_Projet
Console.WriteLine(fileInfos); Console.WriteLine(fileInfos);
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
int choosenItem = 0; int choosenItem = mainMenu.ShowMenu(0, 9);
return (Operation)choosenItem;
}
public static MyImage PerformOperation(Operation ope, MyImage img)
{
MyImage output = null;
Console.Clear();
for (int i = 0; i < menuItems.Length; i++) switch (ope)
{ {
Console.WriteLine(((choosenItem == i) ? ">> " : " ") + menuItems[i]); case Operation.Enlarge:
output = EnlargeOperation(img);
break;
case Operation.Shrink:
output = ShrinkOperation(img);
break;
case Operation.Rotate90:
output = img.Rotate90();
break;
case Operation.Rotate180:
output = img.Rotate180();
break;
case Operation.Rotate270:
output = img.Rotate270();
break;
case Operation.GrayScaleLinear:
output = GrayOperation(img, ope);
break;
case Operation.GrayScaleLuminosity:
output = GrayOperation(img, ope);
break;
case Operation.Superposition:
output = SuperpositionOperation(img);
break;
case Operation.Filter: // TODO
output = FilterOperation(img);
break;
case Operation.Histogram:
output = HistogramOperation(img);
break;
case Operation.Dithering:
output = img.Dithering(1);
break;
case Operation.Debug:
output = DebugOperation(img);
break;
case Operation.Multithreading:
output = MultithreadingOperation(img);
break;
case Operation.Save:
SaveOperation(img);
break;
} }
sw.Stop();
elapsedTime = sw.ElapsedMilliseconds;
lastOperationMessage = String.Format("Opération {0}, effectuée avec succès en {1}ms", ope, elapsedTime);
fileInfos = (output != null) ? output.ToString() : "";
return output;
}
public static MyImage EnlargeOperation(MyImage img)
{
int coeff = 0;
bool error = false;
do
{
if (error) AlertMessage("Un nombre entier supérieur à 0 est attendu.");
Console.WriteLine("Quel coefficient d'aggrandissement souhaitez-vous utiliser ?");
} while (error = !int.TryParse(Console.ReadLine(), out coeff) || coeff <= 0);
sw.Restart();
return img.Enlarge(coeff);
}
public static MyImage ShrinkOperation(MyImage img)
{
int coeff = 0;
bool error = false;
do
{
if (error) AlertMessage("Un nombre entier supérieur à 0 est attendu.");
Console.WriteLine("Quel coefficient de rétrécissement souhaitez-vous utiliser ?");
} while (error = !int.TryParse(Console.ReadLine(), out coeff) && coeff <= 0);
sw.Restart();
return img.Shrink(coeff);
}
public static MyImage GrayOperation(MyImage img, Operation ope)
{
int nbScale = 0;
bool error = false;
do
{
if (error) AlertMessage("Un nombre entier supérieur à 1 et inférieur ou égal à 255 est attendu.");
Console.WriteLine("Combien de nuances de gris souhaitez-vous obtenir ?");
} while (error = !int.TryParse(Console.ReadLine(), out nbScale) && nbScale <= 1 && nbScale > 255);
ConsoleKeyInfo key; sw.Restart();
if (ope == Operation.GrayScaleLinear) return img.ToGrayScale((byte)nbScale, MyImage.GrayFilterType.LINEAR);
else return img.ToGrayScale((byte)nbScale, MyImage.GrayFilterType.LUMINOSITY);
}
public static MyImage SuperpositionOperation(MyImage img)
{
string path = "";
MyImage secondImg = null;
byte[] file = null;
int x = 0;
int y = 0;
bool error = true;
// Get path to image
do do
{ {
key = Console.ReadKey(); error = false;
Console.SetCursorPosition(0, 9 + choosenItem); Console.WriteLine("Veuillez saisir le chemin de l'image que vous voulez superposer à cette image :");
Console.Write(" "); path = Console.ReadLine();
if (key.Key == ConsoleKey.DownArrow) try
{ {
choosenItem++; file = File.ReadAllBytes(path);
if (choosenItem == menuItems.Length) choosenItem = 0;
} }
else if (key.Key == ConsoleKey.UpArrow) catch (Exception e)
{ {
choosenItem--; error = true;
if (choosenItem == -1) choosenItem = menuItems.Length - 1; AlertMessage("Une erreur est survenue : \n" + e.Message);
} }
Console.SetCursorPosition(0, 9 + choosenItem); } while (error);
Console.Write(">>");
} while (key.Key != ConsoleKey.Escape && key.Key != ConsoleKey.Enter);
if (key.Key == ConsoleKey.Escape) Environment.Exit(0); secondImg = new MyImage(file);
return (Operation)choosenItem; do
{
if (error) AlertMessage("Un nombre entier est attendu.");
Console.WriteLine("A quelle coordonnée x souhaitez vous placer l'image à superposer ?");
} while (error = !int.TryParse(Console.ReadLine(), out x));
do
{
if (error) AlertMessage("Un nombre entier est attendu.");
Console.WriteLine("A quelle coordonnée y souhaitez vous placer l'image à superposer ?");
} while (error = !int.TryParse(Console.ReadLine(), out y));
sw.Restart();
return img.Superposition(secondImg, x, y);
} }
/// <summary> public static MyImage FilterOperation(MyImage img)
/// Effectue l'opération demandée sur l'image passée en paramètres
/// </summary>
/// <param name="ope">Opération désirée</param>
/// <param name="img">Image sur laquelle effectuer l'opération</param>
/// <returns>Image avec l'opération effectuée dessus</returns>
public static MyImage PerformOperation(Operation ope, MyImage img)
{ {
MyImage output = null; int choix;
Stopwatch sw = new Stopwatch(); MyImage newImage;
Console.Clear(); Console.Clear();
Console.WriteLine("Quel filtre souhaitez-vous appliquer ?");
choix = filterMenu.ShowMenu(0,3);
sw.Restart();
newImage = img.Filter(Filter.Filtres[choix], Filter.Facteurs[choix]);
sw.Start(); return newImage;
if (ope == Operation.Rotate90) output = img.Rotate90(); }
else if (ope == Operation.Rotate180) output = img.Rotate180();
else if (ope == Operation.Rotate270) output = img.Rotate270(); public static MyImage HistogramOperation(MyImage img)
else if (ope == Operation.GrayScaleLinear || ope == Operation.GrayScaleLuminosity) {
int width = 0;
bool error = false;
string path = "";
do
{ {
bool retry = false; if (error) AlertMessage("Un nombre entier supérieur à 0 est attendu.");
int input; Console.WriteLine("Quel largeur souhaitez-vous utiliser pour l'histogramme ?");
} while (error = !int.TryParse(Console.ReadLine(), out width) || width <= 0);
do error = false;
{ do
if (retry) Console.WriteLine("Saisie incorrecte"); {
Console.WriteLine("Veuillez saisir le nombre de nuances de gris désirées (entre 2 et 255)"); if (error) AlertMessage("Un chemin valide vers un dossier est attendu");
Console.WriteLine("Veuillez saisir le chemin du sauvegarde des histogrammes :");
path = Console.ReadLine();
} while (error = !Directory.Exists(path));
if (!path.EndsWith("\\")) path += "\\";
retry = !int.TryParse(Console.ReadLine(), out input); sw.Restart();
} while (retry && input >= 2 && input <= 255); MyImage[] histograms = img.Histogram(width);
histograms[0].Save(path + "red.bmp");
histograms[1].Save(path + "green.bmp");
histograms[2].Save(path + "blue.bmp");
sw.Restart(); return img;
if (ope == Operation.GrayScaleLinear) output = img.ToGrayScale((byte)input, MyImage.GrayFilterType.LINEAR); }
else if (ope == Operation.GrayScaleLuminosity) output = img.ToGrayScale((byte)input, MyImage.GrayFilterType.LUMINOSITY);
public static MyImage DebugOperation(MyImage img)
{
sw.Restart();
if (Program.DEBUG)
{
Program.DEBUG = false;
menuItems[(int)Operation.Debug] = "Activer le mode debug";
} }
else if(ope == Operation.Shrink) else
{ {
bool retry = false; Program.DEBUG = true;
int input; menuItems[(int)Operation.Debug] = "Désactiver le mode debug";
}
return img;
}
do public static MyImage MultithreadingOperation(MyImage img)
{ {
if (retry) Console.WriteLine("Saisie incorrecte"); if(Program.MULTITHREADING)
Console.WriteLine("Veuillez saisir le facteur de rétrécissement souhaité"); {
Program.MULTITHREADING = false;
menuItems[(int)Operation.Multithreading] = "Activer le multithreading";
}
else
{
Program.MULTITHREADING = true;
menuItems[(int)Operation.Multithreading] = "Désactiver le multithreading";
}
return img;
}
public static MyImage SaveOperation(MyImage img)
{
string path = "";
Console.WriteLine("Veuillez saisir le chemin de sauvegarde du fichier");
path = Console.ReadLine();
if (!path.EndsWith(".bmp")) path += ".bmp";
retry = !int.TryParse(Console.ReadLine(), out input); img.Save(path);
} while (retry && input >= 2);
sw.Restart(); return null;
output = img.Shrink(input); }
public unsafe static MyImage CreateImageOperation()
{
Options opt = new Options
{
offset = 54,
fileInfoHeaderSize = 40,
bitsPerPixel = 24,
format = "BM"
};
int x, y, form, width, height;
byte[] pixels;
opt.height = opt.width = width = height = x = y = form = 0;
bool error = false;
do opt.height = AskForInteger("Longueur de l'image :", error, "Veuillez saisir un entier supérieur à 0."); while (error = opt.height <= 0);
error = false;
do opt.width = AskForInteger("Largeur de l'image :", error, "Veuillez saisir un entier supérieur à 0."); while (error = opt.width <= 0);
error = false;
do form = AskForInteger("Type de forme :\n1)Rectangle\n2)Cercle :", error, "Veuillez saisir un entier égal à 1 ou 2."); while (error = (form != 1 && form != 2));
error = false;
do x = AskForInteger("Coordonnée x du centre de la forme :", error, "Veuillez saisir un entier supérieur ou égal à 0 et inférieur à " + opt.width + "."); while (error = (x < 0 || x >= opt.width));
error = false;
do y = AskForInteger("Coordonnée y du centre de la forme :", error, "Veuillez saisir un entier supérieur ou égal à 0 et inférieur à " + opt.width + "."); while (error = (y < 0 || y >= opt.width));
error = false;
if (form == 1)
{
do height = AskForInteger("Longueur du rectangle :", error, "Veuillez saisir un entier supérieur à 0."); while (error = height <= 0);
error = false;
do width = AskForInteger("Largeur du rectangle :", error, "Veuillez saisir un entier supérieur à 0."); while (error = width <= 0);
error = false;
} }
else if(ope == Operation.SwitchMultiThreading) else if (form == 2)
{ {
Program.MULTITHREADING = !Program.MULTITHREADING; do width = AskForInteger("Rayon du cercle :", error, "Veuillez saisir un entier supérieur à 0."); while (error = width <= 0);
if (Program.MULTITHREADING) error = false;
}
pixels = new byte[opt.width * opt.height];
opt.fileSize = opt.offset + opt.width * opt.height * 3;
sw.Restart();
fixed (byte* pixelsPtr = pixels)
{
if (form == 1)
{ {
lastOperationMessage = "Multithreading activé avec succès"; int x0 = x - width / 2;
menuItems[(int)ope] = "Désactiver le multithreading"; int y0 = y - height / 2;
for (int i = 0; i < width; i++)
{
if (x0 + i > 0 && x0 + i < opt.width)
{
if (y0 >= 0)
*(pixelsPtr + x0 + i + y0 * opt.width) = 255;
if (y0 + height < opt.height)
*(pixelsPtr + x0 + i + (y0 + height) * opt.width) = 255;
}
}
for (int i = 0; i < height; i++)
{
if (y0 + i > 0 && y0 + i < opt.height)
{
if (x0 >= 0)
*(pixelsPtr + x0 + (y0 + i) * opt.width) = 255;
if (x0 + width < opt.width)
*(pixelsPtr + x0 + width + (y0 + i) * opt.width) = 255;
}
}
} }
else else
{ {
lastOperationMessage = "Multithreading désactivé avec succès"; int x0;
menuItems[(int)ope] = "Activer le multithreading"; int y0;
} for (double i = 0; i < 360; i+= 0.001)
elapsedTime = 0; {
return img; x0 = x + (int)(width * Math.Cos(i));
} y0 = y + (int)(width * Math.Sin(i));
else if (ope == Operation.Save)
{
string path;
bool anotherTry = false;
do *(pixelsPtr + x0 + y0 * opt.width) = 255;
{ }
if (anotherTry) Console.WriteLine("Le format du chemin est incorrect"); }
Console.WriteLine("Veuillez saisir le nom du fichier en sortie");
path = Console.ReadLine();
if (!path.EndsWith(".bmp")) path += ".bmp";
} while (anotherTry = !Uri.IsWellFormedUriString(path, UriKind.RelativeOrAbsolute));
sw.Restart();
img.Save(path);
}
else
{
throw new Exception("Opération non reconnue");
} }
sw.Stop(); return new MyImage(opt, pixels, pixels, pixels);
elapsedTime = sw.ElapsedMilliseconds; }
lastOperationMessage = String.Format("Opération {0}, effectuée avec succès en {1}ms", ope, elapsedTime);
fileInfos = (output != null) ? output.ToString() : "";
return output; public static void AlertMessage(string msg)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(msg);
Console.ForegroundColor = ConsoleColor.White;
} }
/// <summary> public static int AskForInteger(string msg, bool error, string errorMsg)
/// Ecran d'attente
/// </summary>
public static void WaitingScreen()
{ {
Console.Write("Traitement de l'image en cours"); int result = 0;
while(true) do
{ {
Console.Write("."); if (error) AlertMessage(errorMsg);
Thread.Sleep(250); Console.WriteLine(msg);
}
} while (error = !int.TryParse(Console.ReadLine(), out result));
return result;
} }
} }
} }
...@@ -6,7 +6,7 @@ using System.Threading.Tasks; ...@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace S04_Projet namespace S04_Projet
{ {
class Filter public class Filter
{ {
public static string[] Noms = new string[] { public static string[] Noms = new string[] {
"Augmenter le contraste", "Augmenter le contraste",
......
...@@ -47,11 +47,11 @@ ...@@ -47,11 +47,11 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Filter.cs" /> <Compile Include="Filter.cs" />
<Compile Include="Menu.cs" />
<Compile Include="MultiThreadedTask.cs" /> <Compile Include="MultiThreadedTask.cs" />
<Compile Include="MyImage.cs" /> <Compile Include="MyImage.cs" />
<Compile Include="Display.cs" /> <Compile Include="Display.cs" />
<Compile Include="Options.cs" /> <Compile Include="Options.cs" />
<Compile Include="Pixel.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace S04_Projet
{
class Menu
{
private string[] menuItems;
public Menu(string[] menuItems)
{
this.menuItems = menuItems;
}
public int ShowMenu(int xOffset, int yOffset)
{
int choosenItem = 0;
Console.SetCursorPosition(xOffset, yOffset);
for (int i = 0; i < menuItems.Length; i++)
{
Console.WriteLine(((choosenItem == i) ? ">> " : " ") + menuItems[i]);
}
ConsoleKeyInfo key;
do
{
key = Console.ReadKey();
Console.SetCursorPosition(xOffset, yOffset + choosenItem);
Console.Write(" ");
if (key.Key == ConsoleKey.DownArrow)
{
choosenItem++;
if (choosenItem == menuItems.Length) choosenItem = 0;
}
else if (key.Key == ConsoleKey.UpArrow)
{
choosenItem--;
if (choosenItem == -1) choosenItem = menuItems.Length - 1;
}
Console.SetCursorPosition(xOffset, yOffset + choosenItem);
Console.Write(">>");
} while (key.Key != ConsoleKey.Escape && key.Key != ConsoleKey.Enter);
if (key.Key == ConsoleKey.Escape) Environment.Exit(0);
return choosenItem;
}
}
}
...@@ -3,13 +3,14 @@ using System.Threading; ...@@ -3,13 +3,14 @@ using System.Threading;
namespace S04_Projet namespace S04_Projet
{ {
class MultiThreadedTask unsafe class MultiThreadedTask
{ {
#region Attributs #region Attributs
public enum Operation public enum Operation
{ {
Load, Load,
Filter Filter,
Save
} }
private enum RGB private enum RGB
...@@ -20,52 +21,83 @@ namespace S04_Projet ...@@ -20,52 +21,83 @@ namespace S04_Projet
} }
private byte[] file; private byte[] file;
private byte[] NewRMatrix, NewGMatrix, NewBMatrix;
byte* rPixelPtr, gPixelPtr, bPixelPtr;
byte* filePtr;
private int nbPixel; private int nbPixel;
private int nbProcessors; private int nbProcessors;
private Pixel[,] Pixels;
private Pixel[,] NewPixels;
private Options opt; private Options opt;
private short[,] filter; private short[,] filter;
private double factor; private double factor;
private int size; private int size;
private Operation ToDo;
#endregion #endregion
public void setNbPixel(int nbPixel) => this.nbPixel = nbPixel; public void SetNbPixel(int nbPixel) => this.nbPixel = nbPixel;
public void setFile(byte[] file) => this.file = file;
private Operation ToDo;
public MultiThreadedTask(Operation ToDo, byte[] file, Options opt) public void SetFile(byte[] file) => this.file = file;
{
this.ToDo = ToDo;
this.opt = opt;
this.file = file;
nbPixel = opt.width * opt.height;
nbProcessors = Environment.ProcessorCount;
}
public MultiThreadedTask(Operation ToDo, Pixel[,] Pixels, Options opt, short[,] filter, double factor) /// <summary>
/// Constructeur pour l'opération Filter
/// </summary>
/// <param name="ToDo"></param>
/// <param name="rPixelPtr"></param>
/// <param name="gPixelPtr"></param>
/// <param name="bPixelPtr"></param>
/// <param name="opt"></param>
/// <param name="filter"></param>
/// <param name="factor"></param>
public unsafe MultiThreadedTask(Operation ToDo, byte* rPixelPtr, byte* gPixelPtr, byte* bPixelPtr, Options opt, short[,] filter, double factor)
{ {
this.ToDo = ToDo; if (ToDo == Operation.Filter)
this.opt = opt; {
this.Pixels = Pixels; this.ToDo = ToDo;
this.filter = filter; this.opt = opt;
this.factor = factor; this.filter = filter;
size = filter.GetLength(0); this.factor = factor;
this.rPixelPtr = rPixelPtr;
this.gPixelPtr = gPixelPtr;
this.bPixelPtr = bPixelPtr;
nbPixel = opt.width * opt.height;
size = filter.GetLength(0);
if (Program.MULTITHREADING)
nbProcessors = Program.MAX_THREADS;
else
nbProcessors = 1;
}
else throw new Exception("Impossible d'utiliser ce constructeur pour ce type d'opération");
} }
public void Start() /// <summary>
/// Constructeur pour l'opération Load
/// </summary>
/// <param name="ToDo"></param>
/// <param name="opt"></param>
/// <param name="rPixelPtr"></param>
/// <param name="gPixelPtr"></param>
/// <param name="bPixelPtr"></param>
/// <param name="filePtr"></param>
public unsafe MultiThreadedTask(Operation ToDo, Options opt, byte* rPixelPtr, byte* gPixelPtr, byte* bPixelPtr, byte* filePtr)
{ {
if (ToDo == Operation.Load) LoadImage(); if (ToDo == Operation.Load || ToDo == Operation.Save)
else if (ToDo == Operation.Filter) ApplyFilter(); {
this.rPixelPtr = rPixelPtr;
this.gPixelPtr = gPixelPtr;
this.bPixelPtr = bPixelPtr;
this.ToDo = ToDo;
this.opt = opt;
this.filePtr = filePtr;
nbPixel = opt.width * opt.height;
nbProcessors = Math.Min(Environment.ProcessorCount, 8);
}
else throw new Exception("Impossible d'utiliser ce constructeur pour ce type d'opération");
} }
#region LoadImage #region Load
public void LoadImage() public unsafe void LoadImage()
{ {
Pixels = new Pixel[opt.width, opt.height];
Thread[] threads = new Thread[nbProcessors]; Thread[] threads = new Thread[nbProcessors];
for (int i = 0; i < nbProcessors; i++) for (int i = 0; i < nbProcessors; i++)
{ {
threads[i] = new Thread(new ParameterizedThreadStart(LoadImageTask)); threads[i] = new Thread(new ParameterizedThreadStart(LoadImageTask));
...@@ -76,38 +108,38 @@ namespace S04_Projet ...@@ -76,38 +108,38 @@ namespace S04_Projet
threads[i].Join(); threads[i].Join();
} }
public void LoadImageTask(object i) private unsafe void LoadImageTask(object i)
{ {
if (file != null && Pixels != null) int start = nbPixel / nbProcessors * (int)i;
{ int end = nbPixel / nbProcessors * ((int)i + 1);
int start = nbPixel / nbProcessors * (int)i;
int end = nbPixel / nbProcessors * ((int)i + 1);
int x, y; int x, y;
byte r, g, b;
for (int j = start; j < end; j++)
{
x = j % opt.width;
y = j / opt.width;
b = file[j * 3 + opt.padding * y + opt.offset];
g = file[j * 3 + opt.padding * y + opt.offset + 1];
r = file[j * 3 + opt.padding * y + opt.offset + 2];
Pixels[x, y] = new Pixel(r, g, b); for (int j = start; j < end; j++)
} {
x = j % opt.width;
y = j / opt.width;
*(bPixelPtr + j) = *(filePtr + opt.offset + opt.padding * y + j * 3);
*(gPixelPtr + j) = *(filePtr + opt.offset + opt.padding * y + j * 3 + 1);
*(rPixelPtr + j) = *(filePtr + opt.offset + opt.padding * y + j * 3 + 2);
} }
} }
#endregion #endregion
#region Filter #region Filter
public void ApplyFilter() public MyImage ApplyFilter()
{ {
NewPixels = new Pixel[opt.width, opt.height]; int nbPixel = opt.width * opt.height;
int pixelNumber = opt.width * opt.height; int pixelPerThread = nbPixel / nbProcessors;
nbProcessors = Environment.ProcessorCount; NewRMatrix = new byte[nbPixel];
int pixelPerThread = pixelNumber / nbProcessors; NewGMatrix = new byte[nbPixel];
NewBMatrix = new byte[nbPixel];
Thread[] threads = new Thread[nbProcessors]; Thread[] threads = new Thread[nbProcessors];
Console.WriteLine("Creating {0} thread(s)", nbProcessors);
for (int i = 0; i < nbProcessors; i++) for (int i = 0; i < nbProcessors; i++)
{ {
threads[i] = new Thread(new ParameterizedThreadStart(ApplyFilterTask)); threads[i] = new Thread(new ParameterizedThreadStart(ApplyFilterTask));
...@@ -118,13 +150,17 @@ namespace S04_Projet ...@@ -118,13 +150,17 @@ namespace S04_Projet
{ {
threads[i].Join(); threads[i].Join();
} }
return new MyImage(opt, NewRMatrix, NewGMatrix, NewBMatrix);
} }
public unsafe void ApplyFilterTask(object i) private unsafe void ApplyFilterTask(object i)
{ {
int x, y; int x, y;
int start = opt.width * opt.height / nbProcessors * (int)i; int nbPixel = opt.width * opt.height;
int end = opt.width * opt.height / nbProcessors * ((int)i + 1); int start = nbPixel / nbProcessors * (int)i;
int end = nbPixel / nbProcessors * ((int)i + 1);
byte[,] rMatrix = new byte[size, size]; byte[,] rMatrix = new byte[size, size];
byte[,] gMatrix = new byte[size, size]; byte[,] gMatrix = new byte[size, size];
byte[,] bMatrix = new byte[size, size]; byte[,] bMatrix = new byte[size, size];
...@@ -146,7 +182,9 @@ namespace S04_Projet ...@@ -146,7 +182,9 @@ namespace S04_Projet
g = ConvolutionalResult(gMatrixPtr, filterPtr, size, factor); g = ConvolutionalResult(gMatrixPtr, filterPtr, size, factor);
b = ConvolutionalResult(bMatrixPtr, filterPtr, size, factor); b = ConvolutionalResult(bMatrixPtr, filterPtr, size, factor);
NewPixels[x, y] = new Pixel(r, g, b); NewRMatrix[j] = r;
NewGMatrix[j] = g;
NewBMatrix[j] = b;
} }
} }
} }
...@@ -173,11 +211,11 @@ namespace S04_Projet ...@@ -173,11 +211,11 @@ namespace S04_Projet
else if (y >= opt.height) y = opt.height - 1; else if (y >= opt.height) y = opt.height - 1;
if (rgb == RGB.R) if (rgb == RGB.R)
*(matrixPtr + i) = Pixels[x, y].r; *(matrixPtr + i) = *(rPixelPtr + x + y * opt.width);
else if (rgb == RGB.G) else if (rgb == RGB.G)
*(matrixPtr + i) = Pixels[x, y].g; *(matrixPtr + i) = *(gPixelPtr + x + y * opt.width);
else if (rgb == RGB.B) else if (rgb == RGB.B)
*(matrixPtr + i) = Pixels[x, y].b; *(matrixPtr + i) = *(bPixelPtr + x + y * opt.width);
} }
} }
...@@ -203,14 +241,38 @@ namespace S04_Projet ...@@ -203,14 +241,38 @@ namespace S04_Projet
} }
#endregion #endregion
public Pixel[,] getPixelMatrix() #region Save
public unsafe void SaveImage()
{ {
return Pixels; Thread[] threads = new Thread[nbProcessors];
for (int i = 0; i < nbProcessors; i++)
{
threads[i] = new Thread(new ParameterizedThreadStart(SaveImageTask));
threads[i].Start(i);
}
for (int i = 0; i < nbProcessors; i++)
threads[i].Join();
} }
public Pixel[,] getFilteredMatrix() private unsafe void SaveImageTask(object i)
{ {
return NewPixels; int start = nbPixel / nbProcessors * (int)i;
int end = nbPixel / nbProcessors * ((int)i + 1);
int x, y;
for (int j = start; j < end; j++)
{
x = j % opt.width;
y = j / opt.width;
*(filePtr + opt.offset + opt.padding * y + j * 3) = *(bPixelPtr + j);
*(filePtr + opt.offset + opt.padding * y + j * 3 + 1) = *(gPixelPtr + j);
*(filePtr + opt.offset + opt.padding * y + j * 3 + 2) = *(rPixelPtr + j);
}
} }
#endregion
} }
} }
...@@ -6,9 +6,12 @@ namespace S04_Projet ...@@ -6,9 +6,12 @@ namespace S04_Projet
{ {
public class MyImage public class MyImage
{ {
#region Attributs
private Options opt; private Options opt;
private Pixel[,] Pixels; byte[] rPixels, gPixels, bPixels;
byte[] file; byte[] file;
private int nbProcessors;
public enum GrayFilterType public enum GrayFilterType
{ {
...@@ -16,18 +19,15 @@ namespace S04_Projet ...@@ -16,18 +19,15 @@ namespace S04_Projet
LUMINOSITY LUMINOSITY
} }
private enum RGB public enum RGB
{ {
R, R,
G, G,
B B
} }
#endregion
public Options getOptions() #region Constructeurs & Save
{
return opt;
}
/// <summary> /// <summary>
/// Constructeur prenant en paramètre un chemin vers un fichier BMP 24 bits /// Constructeur prenant en paramètre un chemin vers un fichier BMP 24 bits
/// </summary> /// </summary>
...@@ -49,6 +49,7 @@ namespace S04_Projet ...@@ -49,6 +49,7 @@ namespace S04_Projet
{ {
if (file.Length != 0) if (file.Length != 0)
{ {
nbProcessors = Environment.ProcessorCount;
FromFileToImage(); FromFileToImage();
} }
} }
...@@ -69,10 +70,12 @@ namespace S04_Projet ...@@ -69,10 +70,12 @@ namespace S04_Projet
/// </summary> /// </summary>
/// <param name="opt">Options de l'image</param> /// <param name="opt">Options de l'image</param>
/// <param name="Pixels">Matrice de pixels constituant l'image</param> /// <param name="Pixels">Matrice de pixels constituant l'image</param>
public MyImage(Options opt, Pixel[,] Pixels) public MyImage(Options opt, byte[] rPixels, byte[] gPixels, byte[] bPixels)
{ {
this.opt = opt; this.opt = opt;
this.Pixels = Pixels; this.rPixels = rPixels;
this.gPixels = gPixels;
this.bPixels = bPixels;
} }
/// <summary> /// <summary>
...@@ -81,7 +84,7 @@ namespace S04_Projet ...@@ -81,7 +84,7 @@ namespace S04_Projet
private unsafe void FromFileToImage() private unsafe void FromFileToImage()
{ {
opt = new Options(); opt = new Options();
#region Header #region Header
opt.format = (char)file[0] + "" + (char)file[1]; opt.format = (char)file[0] + "" + (char)file[1];
opt.fileSize = EndianToInt(file, 2); opt.fileSize = EndianToInt(file, 2);
...@@ -103,27 +106,24 @@ namespace S04_Projet ...@@ -103,27 +106,24 @@ namespace S04_Projet
#endregion #endregion
#region Pixel array #region Pixel array
int pixelNumber = opt.width * opt.height; int pixelNumber = opt.width * opt.height;
Pixels = new Pixel[opt.width, opt.height]; rPixels = new byte[pixelNumber];
gPixels = new byte[pixelNumber];
bPixels = new byte[pixelNumber];
#region Mono #region Mono
if (!Program.MULTITHREADING_LOAD) if (!Program.MULTITHREADING)
{ {
int x, y; int y;
byte r, g, b; fixed (byte* filePtr = file, rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
fixed (byte* filePtr = file)
{ {
for (int i = 0; i < pixelNumber; i++) for (int i = 0; i < pixelNumber; i++)
{ {
x = i % opt.width;
y = i / opt.width; y = i / opt.width;
/*b = file[i * 3 + opt.padding * y + opt.offset];
g = file[i * 3 + opt.padding * y + opt.offset + 1]; *(rPtr + i) = *(filePtr + i * 3 + opt.padding * y + opt.offset + 2);
r = file[i * 3 + opt.padding * y + opt.offset + 2];*/ *(gPtr + i) = *(filePtr + i * 3 + opt.padding * y + opt.offset + 1);
b = *(filePtr + i * 3 + opt.padding * y + opt.offset); *(bPtr + i) = *(filePtr + i * 3 + opt.padding * y + opt.offset);
g = *(filePtr + i * 3 + opt.padding * y + opt.offset + 1);
r = *(filePtr + i * 3 + opt.padding * y + opt.offset + 2);
Pixels[x, y] = new Pixel(r, g, b);
} }
} }
} }
...@@ -132,9 +132,11 @@ namespace S04_Projet ...@@ -132,9 +132,11 @@ namespace S04_Projet
#region Multithreading #region Multithreading
else else
{ {
MultiThreadedTask LoadImageTask = new MultiThreadedTask(MultiThreadedTask.Operation.Load, file, opt); fixed (byte* filePtr = file, rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
LoadImageTask.Start(); {
Pixels = LoadImageTask.getPixelMatrix(); MultiThreadedTask LoadImageTask = new MultiThreadedTask(MultiThreadedTask.Operation.Load, opt, rPtr, gPtr, bPtr, filePtr);
LoadImageTask.LoadImage();
}
} }
#endregion #endregion
...@@ -147,13 +149,13 @@ namespace S04_Projet ...@@ -147,13 +149,13 @@ namespace S04_Projet
/// Sauvegarde l'image vers le chemin passé en paramètres /// Sauvegarde l'image vers le chemin passé en paramètres
/// </summary> /// </summary>
/// <param name="output">Chemin de sortie de l'image</param> /// <param name="output">Chemin de sortie de l'image</param>
private void FromImageToFile(string output) private unsafe void FromImageToFile(string output)
{ {
int nbPixel = (opt.height * opt.width); long nbPixel = (opt.height * opt.width);
int bytesNumber = nbPixel * 3 + opt.offset + opt.height * opt.padding; // Multiply by 3 because 3 bytes / pixel long bytesNumber = nbPixel * 3 + opt.offset + opt.height * opt.padding; // Multiply by 3 because 3 bytes / pixel
byte[] file = new byte[bytesNumber]; byte[] file = new byte[bytesNumber];
#region HEADER #region HEADER
// FORMAT // FORMAT
ASCIIEncoding encoding = new ASCIIEncoding(); ASCIIEncoding encoding = new ASCIIEncoding();
...@@ -163,7 +165,7 @@ namespace S04_Projet ...@@ -163,7 +165,7 @@ namespace S04_Projet
// OFFSET // OFFSET
MixArrays(file, IntToEndian(opt.offset), 10); MixArrays(file, IntToEndian(opt.offset), 10);
#endregion #endregion
#region File Info Header #region File Info Header
MixArrays(file, IntToEndian(opt.fileInfoHeaderSize), 14); MixArrays(file, IntToEndian(opt.fileInfoHeaderSize), 14);
MixArrays(file, IntToEndian(opt.width), 18); MixArrays(file, IntToEndian(opt.width), 18);
...@@ -177,19 +179,45 @@ namespace S04_Projet ...@@ -177,19 +179,45 @@ namespace S04_Projet
MixArrays(file, IntToEndian(opt.nbColor), 46); MixArrays(file, IntToEndian(opt.nbColor), 46);
MixArrays(file, IntToEndian(0), 50); // Number of important colors MixArrays(file, IntToEndian(0), 50); // Number of important colors
#endregion #endregion
#region Pixel array #region Pixel array
int x; if (!Program.MULTITHREADING)
int y; {
for (int i = 0; i < nbPixel; i++) int x;
int y;
fixed (byte* filePtr = file)
{
for (int i = 0; i < nbPixel; i++)
{
x = i % opt.width;
y = i / opt.width;
*(filePtr + i * 3 + opt.padding * y + opt.offset) = bPixels[i];
*(filePtr + i * 3 + opt.padding * y + opt.offset + 1) = gPixels[i];
*(filePtr + i * 3 + opt.padding * y + opt.offset + 2) = rPixels[i];
}
}
}
else
{ {
x = i % opt.width; fixed (byte* filePtr = file, rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
y = i / opt.width; {
MixArrays(file, Pixels[x, y].getBGR(), opt.offset + (i * 3) + opt.padding * y); MultiThreadedTask SaveImageTask = new MultiThreadedTask(MultiThreadedTask.Operation.Save, opt, rPtr, gPtr, bPtr, filePtr);
SaveImageTask.SaveImage();
}
} }
#endregion #endregion
File.WriteAllBytes(output, file); try
{
File.WriteAllBytes(output, file);
}
catch (Exception e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Une erreur est survenue lors de la sauvegarde du fichier : \n" + e.Message);
}
} }
/// <summary> /// <summary>
...@@ -200,92 +228,192 @@ namespace S04_Projet ...@@ -200,92 +228,192 @@ namespace S04_Projet
{ {
FromImageToFile(output); FromImageToFile(output);
} }
#endregion
#region Image processing
#region Rotations #region Rotations
/// <summary> /// <summary>
/// Tourne l'image de 90° dans le sens horaire /// Tourne l'image de 90° dans le sens horaire
/// </summary> /// </summary>
/// <returns>Image tournée de 90° dans le sens horaire</returns> /// <returns>Image tournée de 90° dans le sens horaire</returns>
public MyImage Rotate90() public unsafe MyImage Rotate90()
{ {
Options options = opt.Copy(); Options options = opt.Copy();
options.width = opt.height; options.width = opt.height;
options.height = opt.width; options.height = opt.width;
Pixel[,] PixelArr = new Pixel[options.width, options.height];
for (int i = 0; i < options.width * options.height; i++) int size = opt.height * opt.width;
byte[] newRPixel = new byte[size];
byte[] newGPixel = new byte[size];
byte[] newBPixel = new byte[size];
fixed (byte* newRPixePtr = newRPixel, newGPixePtr = newGPixel, newBPixePtr = newBPixel)
{ {
int x = i % opt.width; fixed (byte* rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
int y = i / opt.width; {
PixelArr[y, options.height - 1 - x] = Pixels[x, y]; for (int i = 0; i < size; i++)
{
int x = i % opt.width;
int y = i / opt.width;
*(newRPixePtr + y + (options.height - 1 - x) * options.width) = *(rPtr + i);
*(newGPixePtr + y + (options.height - 1 - x) * options.width) = *(gPtr + i);
*(newBPixePtr + y + (options.height - 1 - x) * options.width) = *(bPtr + i);
}
}
} }
return new MyImage(options, PixelArr); return new MyImage(options, newRPixel, newGPixel, newBPixel);
} }
/// <summary> /// <summary>
/// Tourne l'image de 180° dans le sens horaire /// Tourne l'image de 180° dans le sens horaire
/// </summary> /// </summary>
/// <returns>Image tournée de 180° dans le sens horaire</returns> /// <returns>Image tournée de 180° dans le sens horaire</returns>
public MyImage Rotate180() public unsafe MyImage Rotate180()
{ {
Options options = opt.Copy(); Options options = opt.Copy();
Pixel[,] PixelArr = new Pixel[options.width, options.height]; int size = opt.height * opt.width;
for (int i = 0; i < options.width * options.height; i++)
byte[] newRPixel = new byte[size];
byte[] newGPixel = new byte[size];
byte[] newBPixel = new byte[size];
fixed (byte* newRPixePtr = newRPixel, newGPixePtr = newGPixel, newBPixePtr = newBPixel)
{ {
int x = i % opt.width; fixed (byte* rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
int y = i / opt.width; {
PixelArr[options.width - x - 1, options.height - y - 1] = Pixels[x, y]; for (int i = 0; i < options.width * options.height; i++)
{
int x = i % opt.width;
int y = i / opt.width;
*(newRPixePtr - x - 1 + (options.height - y) * options.width) = *(rPtr + i);
*(newGPixePtr - x - 1 + (options.height - y) * options.width) = *(gPtr + i);
*(newBPixePtr - x - 1 + (options.height - y) * options.width) = *(bPtr + i);
}
}
} }
return new MyImage(options, PixelArr); return new MyImage(options, newRPixel, newGPixel, newBPixel);
} }
/// <summary> /// <summary>
/// Tourne l'image de 270° dans le sens horaire /// Tourne l'image de 270° dans le sens horaire
/// </summary> /// </summary>
/// <returns>Image tournée de 270° dans le sens horaire</returns> /// <returns>Image tournée de 270° dans le sens horaire</returns>
public MyImage Rotate270() public unsafe MyImage Rotate270()
{ {
Options options = opt.Copy(); Options options = opt.Copy();
options.width = opt.height; options.width = opt.height;
options.height = opt.width; options.height = opt.width;
Pixel[,] PixelArr = new Pixel[options.width, options.height]; int size = opt.height * opt.width;
for (int i = 0; i < options.width * options.height; i++)
byte[] newRPixel = new byte[size];
byte[] newGPixel = new byte[size];
byte[] newBPixel = new byte[size];
fixed (byte* newRPixePtr = newRPixel, newGPixePtr = newGPixel, newBPixePtr = newBPixel)
{ {
int x = i % opt.width; fixed (byte* rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
int y = i / opt.width; {
PixelArr[options.width - 1 - y, x] = Pixels[x, y]; for (int i = 0; i < options.width * options.height; i++)
{
int x = i % opt.width;
int y = i / opt.width;
*(newRPixePtr + options.width - 1 - y + x * options.width) = *(rPtr + i);
*(newGPixePtr + options.width - 1 - y + x * options.width) = *(gPtr + i);
*(newBPixePtr + options.width - 1 - y + x * options.width) = *(bPtr + i);
//PixelArr[options.width - 1 - y, x] = Pixels[x, y];
}
}
} }
return new MyImage(options, PixelArr);
return new MyImage(options, newRPixel, newGPixel, newBPixel);
} }
#endregion #endregion
/// <summary> /// <summary>
/// Agrandit l'image par rapport au coefficient passé en paramètre. WIP /// Passe l'image en nuances de gris
/// </summary>
/// <param name="scale">Nombre de nuances de gris désirées (entre 2 et 255) (2 = noir et blanc)</param>
/// <param name="type">Type de transformation. Linéaire ou en fonction de la luminosité de la couleur</param>
/// <returns>Image en nuances de gris</returns>
public unsafe MyImage ToGrayScale(byte scale, GrayFilterType type)
{
Options options = opt.Copy();
opt.nbColor = scale;
int size = opt.height * opt.width;
byte[] newRPixel = new byte[size];
byte[] newGPixel = new byte[size];
byte[] newBPixel = new byte[size];
fixed (byte* newRPixePtr = newRPixel, newGPixePtr = newGPixel, newBPixePtr = newBPixel)
{
fixed (byte* rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
{
for (int i = 0; i < opt.width * opt.height; i++)
{
int x = i % opt.width;
int y = i / opt.width;
if (type == GrayFilterType.LINEAR)
{
*(newRPixePtr + x + y * opt.width) =
*(newGPixePtr + x + y * opt.width) =
*(newBPixePtr + x + y * opt.width) = GetGray(*(rPtr + x + y * opt.width), *(gPtr + x + y * opt.width), *(bPtr + x + y * opt.width), scale);
}
else if (type == GrayFilterType.LUMINOSITY)
*(newRPixePtr + x + y * opt.width) =
*(newGPixePtr + x + y * opt.width) =
*(newBPixePtr + x + y * opt.width) = GetGrayLuminosity(*(rPtr + x + y * opt.width), *(gPtr + x + y * opt.width), *(bPtr + x + y * opt.width), scale);
}
}
}
return new MyImage(options, newRPixel, newGPixel, newBPixel);
}
/// <summary>
/// Agrandit l'image par rapport au coefficient passé en paramètre
/// </summary> /// </summary>
/// <param name="coeff">Coefficient d'agrandissement de l'image</param> /// <param name="coeff">Coefficient d'agrandissement de l'image</param>
/// <returns>Image élargie</returns> /// <returns>Image élargie</returns>
public MyImage Enlarge(int coeff) public unsafe MyImage Enlarge(int coeff)
{ {
Options options = opt.Copy(); Options options = opt.Copy();
options.width *= coeff; options.width *= coeff;
options.height *= coeff; options.height *= coeff;
Pixel[,] PixelArr = new Pixel[opt.width * 2, opt.height * 2];
for (int i = 0; i < options.width * options.height; i++)
{
int x = i % options.width;
int y = i % options.height;
byte[] fromX = Pixels[x, y].getRGB(); int nbPixel = opt.width * opt.height;
byte[] toX; int nbNewPixel = options.width * options.height;
byte[] toY;
byte[] toXY;
if (x + 1 < options.width) toX = Pixels[x + 1, y].getRGB(); byte[] newPixelR = new byte[nbNewPixel];
if (y + 1 < options.height) toY = Pixels[x, y + 1].getRGB(); byte[] newPixelG = new byte[nbNewPixel];
if (y + 1 < options.height && x + 1 < options.width) toXY = Pixels[x + 1, y + 1].getRGB(); byte[] newPixelB = new byte[nbNewPixel];
int x, y, x0, y0;
fixed (byte* newRPtr = newPixelR, newGPtr = newPixelG, newBPtr = newPixelB)
{
fixed (byte* rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
{
for (int i = 0; i < nbPixel; i++)
{
x0 = i % opt.width;
y0 = i / opt.width;
for (int j = 0; j < coeff * coeff; j++)
{
x = x0 * coeff + j % coeff;
y = y0 * coeff + j / coeff;
*(newRPtr + x + y * options.width) = *(rPtr + i);
*(newGPtr + x + y * options.width) = *(gPtr + i);
*(newBPtr + x + y * options.width) = *(bPtr + i);
}
}
}
} }
return null;
return new MyImage(options, newPixelR, newPixelG, newPixelB);
} }
/// <summary> /// <summary>
...@@ -293,7 +421,7 @@ namespace S04_Projet ...@@ -293,7 +421,7 @@ namespace S04_Projet
/// </summary> /// </summary>
/// <param name="coeff">Coefficient de rétrécissement de l'image</param> /// <param name="coeff">Coefficient de rétrécissement de l'image</param>
/// <returns>Image rétrécie</returns> /// <returns>Image rétrécie</returns>
public MyImage Shrink(int coeff) public unsafe MyImage Shrink(int coeff)
{ {
Options options = opt.Copy(); Options options = opt.Copy();
options.width /= coeff; options.width /= coeff;
...@@ -302,170 +430,334 @@ namespace S04_Projet ...@@ -302,170 +430,334 @@ namespace S04_Projet
options.imgSize = options.width * options.height * 3 + options.padding * options.height; options.imgSize = options.width * options.height * 3 + options.padding * options.height;
options.fileSize = options.imgSize + options.fileInfoHeaderSize; options.fileSize = options.imgSize + options.fileInfoHeaderSize;
Pixel[,] PixelArr = new Pixel[options.width, options.height]; int size = options.width * options.height;
int x0, y0, x, y; byte[] newRPixel = new byte[size];
int[] rgb; byte[] newGPixel = new byte[size];
byte[] _rgb; byte[] newBPixel = new byte[size];
for (int i = 0; i < options.width * options.height; i++) int x0, y0, x, y;
int r, g, b;
fixed (byte* newRPixePtr = newRPixel, newGPixePtr = newGPixel, newBPixePtr = newBPixel)
{ {
x0 = i % options.width; fixed (byte* rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
y0 = i / options.width; {
for (int i = 0; i < size; i++)
{
x0 = i % options.width;
y0 = i / options.width;
rgb = new int[] { 0, 0, 0 }; r = g = b = 0;
for (int j = 0; j < coeff * coeff; j++) for (int j = 0; j < coeff * coeff; j++)
{ {
x = x0 * coeff + j % coeff; x = x0 * coeff + j % coeff;
y = y0 * coeff + j / coeff; y = y0 * coeff + j / coeff;
_rgb = Pixels[x, y].getRGB(); r += *(rPtr + x + y * opt.width);
for (int k = 0; k < 3; k++) g += *(gPtr + x + y * opt.width);
rgb[k] += _rgb[k]; b += *(bPtr + x + y * opt.width);
} }
for (int j = 0; j < 3; j++) r /= coeff * coeff;
rgb[j] = (byte)(rgb[j] / (coeff * coeff)); g /= coeff * coeff;
b /= coeff * coeff;
PixelArr[x0, y0] = new Pixel(rgb); *(newRPixePtr + x0 + y0 * options.width) = (byte)r;
*(newGPixePtr + x0 + y0 * options.width) = (byte)g;
*(newBPixePtr + x0 + y0 * options.width) = (byte)b;
}
}
} }
return new MyImage(options, PixelArr); return new MyImage(options, newRPixel, newGPixel, newBPixel);
} }
/// <summary> /// <summary>
/// Passe l'image en nuances de gris /// Applique un filtre de convolution sur une image
/// </summary> /// </summary>
/// <param name="scale">Nombre de nuances de gris désirées (entre 2 et 255) (2 = noir et blanc)</param> /// <param name="filter">Filtre à appliquer</param>
/// <param name="type">Type de transformation. Linéaire ou en fonction de la luminosité de la couleur</param> /// <param name="factor">Facteur de normalisation du filtre</param>
/// <returns>Image en nuances de gris</returns> /// <returns>Image modifiée avec le filtre</returns>
public MyImage ToGrayScale(byte scale, GrayFilterType type) public unsafe MyImage Filter(short[,] filter, double factor)
{ {
Options options = opt.Copy(); fixed (byte* rPixelPtr = rPixels, gPixelPtr = gPixels, bPixelPtr = bPixels)
opt.nbColor = scale;
Pixel[,] PixelArr = new Pixel[opt.width, opt.height];
for (int i = 0; i < opt.width * opt.height; i++)
{ {
int x = i % opt.width; MultiThreadedTask multiThreadedTask = new MultiThreadedTask(MultiThreadedTask.Operation.Filter, rPixelPtr, gPixelPtr, bPixelPtr, opt, filter, factor);
int y = i / opt.width; return multiThreadedTask.ApplyFilter();
if (type == GrayFilterType.LINEAR)
PixelArr[x, y] = Pixels[x, y].getGrayScale(scale);
else if (type == GrayFilterType.LUMINOSITY)
PixelArr[x, y] = Pixels[x, y].getGrayScaleLuminosity(scale);
} }
return new MyImage(options, PixelArr);
} }
/// <summary> /// <summary>
/// Applique un filtre de convolution à l'image /// Generate the 3 histograms of the image
/// </summary> /// </summary>
/// <param name="filter">Filtre de convolution</param> /// <param name="width"></param>
/// <param name="factor">Facteur de normalisation</param> /// <returns></returns>
/// <returns>Image avec le filtre de convolution appliqué</returns> public unsafe MyImage[] Histogram(int width)
public MyImage ApplyConvFilter(short[,] filter, double factor)
{ {
if (filter.GetLength(0) == filter.GetLength(1) && filter.GetLength(0) % 2 == 1) MyImage[] Images = new MyImage[3];
{
int size = filter.GetLength(0);
Options options = opt.Copy();
int nbPixel = options.height * options.width;
Pixel[,] PixelArr = new Pixel[options.width, options.height];
if (!Program.MULTITHREADING) int[,] numberOfRGB = new int[3, 256];
{ int nbPixel = opt.width * opt.height;
int x, y;
byte[,] rMatrix, gMatrix, bMatrix;
byte r, g, b;
Options options = new Options
{
width = width,
offset = 54,
fileInfoHeaderSize = 40,
bitsPerPixel = 24,
format = "BM"
};
options.height = (int)(options.width / 1.6180339887);
options.fileSize = options.offset + options.width * options.height * 3;
byte[] newRPixel = new byte[options.width * options.height];
byte[] newGPixel = new byte[options.width * options.height];
byte[] newBPixel = new byte[options.width * options.height];
int pixelsPerValue = options.width / 256;
fixed (int* numberOfRGBPtr = numberOfRGB)
{
fixed (byte* rPixelPtr = rPixels, gPixelPtr = gPixels, bPixelPtr = bPixels)
{
for (int i = 0; i < nbPixel; i++) for (int i = 0; i < nbPixel; i++)
{ {
x = i % options.width; *(numberOfRGBPtr + *(rPixelPtr + i)) += 1;
y = i / options.width; *(numberOfRGBPtr + 256 * 1 + *(gPixelPtr + i)) += 1;
*(numberOfRGBPtr + 256 * 2 + *(bPixelPtr + i)) += 1;
}
}
rMatrix = GetMatrix(x, y, size, RGB.R); int maxR = Max(numberOfRGBPtr, 256);
gMatrix = GetMatrix(x, y, size, RGB.G); int maxG = Max(numberOfRGBPtr + 256, 256);
bMatrix = GetMatrix(x, y, size, RGB.B); int maxB = Max(numberOfRGBPtr + 256 * 2, 256);
r = ConvolutionalResult(rMatrix, filter, size, factor); for (int i = 0; i < 256; i++)
g = ConvolutionalResult(gMatrix, filter, size, factor); {
b = ConvolutionalResult(bMatrix, filter, size, factor); *(numberOfRGBPtr + i) = (*(numberOfRGBPtr + i) * options.height) / maxR;
*(numberOfRGBPtr + 256 * 1 + i) = (*(numberOfRGBPtr + 256 * 1 + i) * options.height) / maxG;
*(numberOfRGBPtr + 256 * 2 + i) = (*(numberOfRGBPtr + 256 * 2 + i) * options.height) / maxB;
}
PixelArr[x, y] = new Pixel(r, g, b); fixed (byte* newRPixelPtr = newRPixel, newGPixelPtr = newGPixel, newBPixelPtr = newBPixel)
{
for (int i = 0; i < 256; i++)
{
for (int j = 0; j < options.height; j++)
{
for (int k = 0; k < pixelsPerValue; k++)
{
if (*(numberOfRGBPtr + i) >= j) *(newRPixelPtr + i * pixelsPerValue + k + j * options.width) = 255;
if (*(numberOfRGBPtr + 256 + i) >= j) *(newGPixelPtr + i * pixelsPerValue + k + j * options.width) = 255;
if (*(numberOfRGBPtr + 265 * 2 + i) >= j) *(newBPixelPtr + i * pixelsPerValue + k + j * options.width) = 255;
}
}
} }
} }
else }
Images[0] = new MyImage(options, newRPixel, new byte[options.width * options.height], new byte[options.width * options.height]);
Images[1] = new MyImage(options, new byte[options.width * options.height], newGPixel, new byte[options.width * options.height]);
Images[2] = new MyImage(options, new byte[options.width * options.height], new byte[options.width * options.height], newBPixel);
return Images;
}
public unsafe MyImage Superposition(MyImage img, int x, int y)
{
byte[] newRPixel = rPixels;
byte[] newGPixel = gPixels;
byte[] newBPixel = bPixels;
fixed (byte* rPixelPtr = img.rPixels, gPixelPtr = img.gPixels, bPixelPtr = img.bPixels)
{
fixed (byte* newRPtr = newRPixel, newGPtr = newGPixel, newBPtr = newBPixel)
{ {
MultiThreadedTask FilterImageTask = for (int i = 0; i < img.opt.height; i++)
new MultiThreadedTask(MultiThreadedTask.Operation.Filter, Pixels, opt, filter, factor); {
FilterImageTask.Start(); if (y + i >= opt.height) break;
Pixel[,] _Pixels = FilterImageTask.getFilteredMatrix(); else if (y + i >= 0)
return new MyImage(opt, _Pixels); for (int j = 0; j < img.opt.width; j++)
{
if (x + j >= opt.width) break;
else if (x + j >= 0)
{
*(newRPtr + x + j + (y + i) * opt.width) = *(rPixelPtr + i * img.opt.width + j);
*(newGPtr + x + j + (y + i) * opt.width) = *(gPixelPtr + i * img.opt.width + j);
*(newBPtr + x + j + (y + i) * opt.width) = *(bPixelPtr + i * img.opt.width + j);
}
}
}
} }
return new MyImage(options, PixelArr);
} }
else if (filter.GetLength(0) != filter.GetLength(1))
return new MyImage(opt, newRPixel, newGPixel, newBPixel);
}
public unsafe MyImage Dithering(byte factor)
{
// cf https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering
int nbPixel = opt.width * opt.height;
int rErr, gErr, bErr;
int newR, newG, newB;
byte[] newRPixel = new byte[nbPixel];
byte[] newGPixel = new byte[nbPixel];
byte[] newBPixel = new byte[nbPixel];
for (int i = 0; i < nbPixel; i++)
{ {
throw new Exception("Matrice non carrée"); newRPixel[i] = rPixels[i];
newGPixel[i] = gPixels[i];
newBPixel[i] = bPixels[i];
} }
else
for (int y = opt.height - 1; y >= 1; y--)
{ {
throw new Exception("Matrice de taille paire"); for (int x = 1; x < opt.width - 1; x++)
{
byte oldR = newRPixel[x + y * opt.width];
byte oldG = newGPixel[x + y * opt.width];
byte oldB = newBPixel[x + y * opt.width];
newR = (int)Math.Round((double)factor * oldR / 255) * (255 / factor);
newG = (int)Math.Round((double)factor * oldG / 255) * (255 / factor);
newB = (int)Math.Round((double)factor * oldB / 255) * (255 / factor);
newRPixel[x + y * opt.width] = (byte)newR;
newGPixel[x + y * opt.width] = (byte)newG;
newBPixel[x + y * opt.width] = (byte)newB;
rErr = oldR - newR;
gErr = oldG - newG;
bErr = oldB - newB;
newRPixel[x + 1 + y * opt.width] = ToByte(newRPixel[x + 1 + y * opt.width] + ((rErr * 7) >> 4));
newGPixel[x + 1 + y * opt.width] = ToByte(newGPixel[x + 1 + y * opt.width] + ((gErr * 7) >> 4));
newBPixel[x + 1 + y * opt.width] = ToByte(newBPixel[x + 1 + y * opt.width] + ((bErr * 7) >> 4));
newRPixel[x - 1 + (y - 1) * opt.width] = ToByte(newRPixel[x - 1 + (y - 1) * opt.width] + ((rErr * 3) >> 4));
newGPixel[x - 1 + (y - 1) * opt.width] = ToByte(newGPixel[x - 1 + (y - 1) * opt.width] + ((gErr * 3) >> 4));
newBPixel[x - 1 + (y - 1) * opt.width] = ToByte(newBPixel[x - 1 + (y - 1) * opt.width] + ((bErr * 3) >> 4));
newRPixel[x + (y - 1) * opt.width] = ToByte(newRPixel[x + (y - 1) * opt.width] + ((rErr * 5) >> 4));
newGPixel[x + (y - 1) * opt.width] = ToByte(newGPixel[x + (y - 1) * opt.width] + ((gErr * 5) >> 4));
newBPixel[x + (y - 1) * opt.width] = ToByte(newBPixel[x + (y - 1) * opt.width] + ((bErr * 5) >> 4));
newRPixel[x + 1 + (y - 1) * opt.width] = ToByte(newRPixel[x + 1 + (y - 1) * opt.width] + (rErr >> 4));
newGPixel[x + 1 + (y - 1) * opt.width] = ToByte(newGPixel[x + 1 + (y - 1) * opt.width] + (gErr >> 4));
newBPixel[x + 1 + (y - 1) * opt.width] = ToByte(newBPixel[x + 1 + (y - 1) * opt.width] + (bErr >> 4));
}
} }
return new MyImage(opt, newRPixel, newGPixel, newBPixel);
} }
private byte ToByte(int i)
{
if (i > 255) return 255;
else if (i < 0) return 0;
else return (byte)i;
}
#endregion
#region Helpers
/// <summary> /// <summary>
/// Retourne la matrice de pixels de taille size ,de milieu (x0,y0) et de couleur RGB /// Returns HSV values of the actual image
/// </summary> /// </summary>
/// <param name="x0">Coordonnée verticale du pixel milieu</param> /// <returns></returns>
/// <param name="y0">Coordonnée horizontale du pixel milieu</param> private unsafe double[,] GetHSV()
/// <param name="size">Taille de la matrice de pixel désirée</param>
/// <param name="rgb">Couleur souhaitée</param>
/// <returns>Matrice de pixels de taille size ,de milieu (x0,y0) et de couleur RGB</returns>
private byte[,] GetMatrix(int x0, int y0, int size, RGB rgb)
{ {
byte[,] matrix = new byte[size, size]; int size = opt.width * opt.height;
double[,] HSV = new double[3, size];
int M, m, C;
double _H = 0;
for (int i = 0; i < size * size; i++) fixed (byte* rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
{ {
int x = x0 + (i % size) - ((size - 1) / 2); fixed (double* hsvPtr = HSV)
int y = y0 + (i / size) - ((size - 1) / 2); {
for (int i = 0; i < size; i++)
if (x < 0) x = 0; {
else if (x >= opt.width) x = opt.width - 1; M = MaxRGB(i);
if (y < 0) y = 0; m = MinRGB(i);
else if (y >= opt.height) y = opt.height - 1; C = M - m;
if (rgb == RGB.R) //Set H value
matrix[(i % size), (i / size)] = Pixels[x, y].r; if (C == 0) _H = 0;
else if (rgb == RGB.G) else if (M == *(rPtr + i)) _H = (((*gPtr + i) - *(bPtr + i)) / C) % 6;
matrix[(i % size), (i / size)] = Pixels[x, y].g; else if (M == *(gPtr + i)) _H = (*(bPtr + i) - *(rPtr + i)) / C + 2;
else if (rgb == RGB.B) else if (M == *(bPtr + i)) _H = (*(rPtr + i) - *(gPtr + i)) / C + 4;
matrix[(i % size), (i / size)] = Pixels[x, y].b;
*(hsvPtr + i) = 60 * _H;
// Set S value
if (M == 0) *(hsvPtr + size + i) = 0;
else *(hsvPtr + size + i) = C / M;
// Set V value
*(hsvPtr + size * 2 + i) = M;
}
}
} }
return matrix; return HSV;
}
/// <summary>
/// Retourne l'équavalent gris d'un pixel
/// </summary>
/// <param name="scale">Nombre de nuances de gris</param>
/// <returns>Pixel en échelle de gris</returns>
private byte GetGray(byte r, byte g, byte b, byte scale)
{
byte total = (byte)((r + g + b) / 3);
total = (byte)(Math.Round((double)total / (255 / (scale - 1))) * (255 / (scale - 1)));
return total;
} }
/// <summary> /// <summary>
/// Calcul convolutionnel /// Retourne l'équivalent gris d'un pixel en prenant en compte les coefficients de luminosité
/// des couleurs RGB
/// </summary> /// </summary>
/// <param name="matrix">Matrice</param> /// <param name="scale"></param>
/// <param name="conv">Convolution</param> /// <returns></returns>
/// <param name="size">Taille du filtre</param> private byte GetGrayLuminosity(byte r, byte g, byte b, byte scale)
/// <param name="factor">Factor de normalisation</param> {
/// <returns>Résultat du calcul convolutinnel</returns> byte total = (byte)(0.21 * r + 0.72 * g + 0.07 * b);
public static byte ConvolutionalResult(byte[,] matrix, short[,] conv, int size, double factor) total = (byte)(Math.Round((double)total / (255 / (scale - 1))) * (255 / (scale - 1)));
return total;
}
private unsafe byte MaxRGB(int i)
{
fixed (byte* rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
{
byte max = *(rPtr + i) > (*gPtr + i) ? *(rPtr + i) : *(gPtr + i);
max = *(bPtr + i) > max ? *(bPtr + i) : max;
return max;
}
}
private unsafe byte MinRGB(int i)
{
fixed (byte* rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
{
byte min = *(rPtr + i) < (*gPtr + i) ? *(rPtr + i) : *(gPtr + i);
min = *(bPtr + i) < min ? *(bPtr + i) : min;
return min;
}
}
private unsafe int Max(int* tab, int length)
{ {
short r = 0; ; int max = 0;
for (int x = 0; x < size; x++) for (int i = 0; i < length; i++)
for (int y = 0; y < size; y++) {
r += (short)(matrix[x, y] * conv[x, y]); if (*(tab + i) > max) max = *(tab + i);
r = (short)Math.Round(r * factor); }
if (r > 255) return 255; return max;
else if (r < 0) return 0;
else return (byte)r;
} }
/// <summary> /// <summary>
...@@ -529,5 +821,96 @@ namespace S04_Projet ...@@ -529,5 +821,96 @@ namespace S04_Projet
return str; return str;
} }
#endregion
#region WIP
/*
public unsafe MyImage EnlargeGradient(int coeff)
{
Options options = opt.Copy();
options.width *= coeff;
options.height *= coeff;
int nbPixel = opt.width * opt.height;
int nbNewPixel = options.width * options.height;
byte[] newPixelR = new byte[nbNewPixel];
byte[] newPixelG = new byte[nbNewPixel];
byte[] newPixelB = new byte[nbNewPixel];
int x, y, x0, y0;
fixed (byte* newRPtr = newPixelR, newGPtr = newPixelG, newBPtr = newPixelB)
{
fixed (byte* rPtr = rPixels, gPtr = gPixels, bPtr = bPixels)
{
for (int i = 0; i < nbPixel; i++)
{
x0 = i % opt.width;
y0 = i / opt.width;
for (int j = 0; j < coeff * coeff; j++)
{
x = x0 * coeff + j % coeff;
y = y0 * coeff + j / coeff;
*(newRPtr + x + y * options.width) = *(rPtr + i);
*(newGPtr + x + y * options.width) = *(gPtr + i);
*(newBPtr + x + y * options.width) = *(bPtr + i);
}
}
}
}
return new MyImage(options, newPixelR, newPixelG, newPixelB);
}
*/
/*
private unsafe byte[] GenerateGradient(int x, int y, int coeff, RGB rgb, byte* rPtr, byte* gPtr, byte* bPtr)
{
// cf https://stackoverflow.com/a/1106986
int squareCoeff = coeff * coeff;
byte[,] grad = new byte[squareCoeff,3];
byte[] A_RGB, B_RGB, C_RGB, D_RGB;
#region Define ABCD
int xPlus = x + 1 < opt.width ? x + 1 : x;
int yPlus = y + 1 < opt.width ? y + 1 : y;
A_RGB = new byte[] {
*(rPtr + x + y * opt.width),
*(gPtr + x + y * opt.width),
*(bPtr + x + y * opt.width)
};
B_RGB = new byte[] {
*(rPtr + xPlus + y * opt.width),
*(gPtr + xPlus + y * opt.width),
*(bPtr + xPlus + y * opt.width)
};
C_RGB = new byte[] {
*(rPtr + xPlus + yPlus * opt.width),
*(gPtr + xPlus + yPlus * opt.width),
*(bPtr + xPlus + yPlus * opt.width)
};
D_RGB = new byte[] {
*(rPtr + x + yPlus * opt.width),
*(gPtr + x + yPlus * opt.width),
*(bPtr + x + yPlus * opt.width)
};
#endregion
int x0, y0;
for (int i = 1; i < squareCoeff; i++)
{
x0 = i % coeff;
y0 = i / coeff;
}
}*/
#endregion
} }
} }
\ No newline at end of file
...@@ -24,7 +24,7 @@ namespace S04_Projet ...@@ -24,7 +24,7 @@ namespace S04_Projet
public int fileSize; public int fileSize;
public int offset; public int offset;
public Options() { } public Options() {}
/// <summary> /// <summary>
/// Return a new instance of Options with the same parameters /// Return a new instance of Options with the same parameters
...@@ -32,22 +32,24 @@ namespace S04_Projet ...@@ -32,22 +32,24 @@ namespace S04_Projet
/// <returns>New option instance</returns> /// <returns>New option instance</returns>
public Options Copy() public Options Copy()
{ {
Options options = new Options(); Options options = new Options
options.fileInfoHeaderSize = fileInfoHeaderSize; {
options.width = width; fileInfoHeaderSize = fileInfoHeaderSize,
options.height = height; width = width,
options.padding = padding; height = height,
options.colorPlanesNb = colorPlanesNb; padding = padding,
options.bitsPerPixel = bitsPerPixel; colorPlanesNb = colorPlanesNb,
options.compressionMethod = compressionMethod; bitsPerPixel = bitsPerPixel,
options.imgSize = imgSize; compressionMethod = compressionMethod,
options.horizontalRes = horizontalRes; imgSize = imgSize,
options.VerticalRes = VerticalRes; horizontalRes = horizontalRes,
options.nbColor = nbColor; VerticalRes = VerticalRes,
options.nbImportantColor = nbImportantColor; nbColor = nbColor,
options.format = format; nbImportantColor = nbImportantColor,
options.fileSize = fileSize; format = format,
options.offset = offset; fileSize = fileSize,
offset = offset
};
return options; return options;
} }
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace S04_Projet
{
public class Pixel
{
public byte r { get; private set; }
public byte g { get; private set; }
public byte b { get; private set; }
public double H { get; private set; }
public double S { get; private set; }
public double L { get; private set; }
public void SetR(byte r) => this.r = r;
public void SetG(byte g) => this.g = g;
public void SetB(byte b) => this.b = b;
/// <summary>
/// Prend les trois couleurs en paramètres
/// </summary>
/// <param name="r">Rouge</param>
/// <param name="g">Vert</param>
/// <param name="b">Bleu</param>
public Pixel(byte r, byte g, byte b)
{
this.r = r;
this.g = g;
this.b = b;
}
public unsafe Pixel(byte* r, byte* g, byte* b)
{
this.r = *r;
this.g = *g;
this.b = *b;
}
/// <summary>
/// Assigne une même valeur à toutes les couleurs
/// </summary>
/// <param name="unifiedColors">Valeur souhaitée pour toutes les couleurs</param>
public Pixel(byte unifiedColors)
{
r = unifiedColors;
g = unifiedColors;
b = unifiedColors;
}
/// <summary>
/// Créer un pixel à partir d'un tableau contenant les couleurs rouge, vert, bleu (respectivement)
/// </summary>
/// <param name="rgb">Tableau composé dans cet ordre des couleurs rouge, vert, bleu</param>
public Pixel(byte[] rgb)
{
r = rgb[0];
g = rgb[1];
b = rgb[2];
}
/// <summary>
/// Créer un pixel à partir d'un tableau contenant les couleurs rouge, vert, bleu (respectivement)
/// </summary>
/// <param name="rgb">Tableau composé dans cet ordre des couleurs rouge, vert, bleu</param>
public Pixel(int[] rgb)
{
r = (byte)rgb[0];
g = (byte)rgb[1];
b = (byte)rgb[2];
}
/// <summary>
/// Retourne un tableau contenant les valeurs RGB du pixel
/// </summary>
/// <returns>Composantes rouge, verte et bleue du pixel</returns>
public byte[] getRGB()
{
return new byte[] { r, g, b };
}
public unsafe byte* getRGBPtr()
{
unsafe
{
fixed (byte* ptr = new byte[] { r, g, b })
return ptr;
}
}
public byte[] getBGR()
{
return new byte[] { b, g, r };
}
/// <summary>
/// Retourne l'équavalent gris d'un pixel
/// </summary>
/// <param name="scale">Nombre de nuances de gris</param>
/// <returns>Pixel en échelle de gris</returns>
public Pixel getGrayScale(byte scale)
{
byte total = (byte)((r + g + b) / 3);
total = (byte)(Math.Round((double)total / (255 / (scale - 1))) * (255 / (scale - 1)));
return new Pixel(total);
}
/// <summary>
/// Retourne l'équivalent gris d'un pixel en prenant en compte les coefficients de luminosité
/// des couleurs RGB
/// </summary>
/// <param name="scale"></param>
/// <returns></returns>
public Pixel getGrayScaleLuminosity(byte scale)
{
byte total = (byte)(0.21 * r + 0.72 * g + 0.07 * b);
total = (byte)(Math.Round((double)total / (255 / (scale - 1))) * (255 / (scale - 1)));
return new Pixel(total);
}
/// <summary>
/// Calcul les valeurs de variables HSL (Hue, Saturation, Lightness)
/// </summary>
private void computeHSL()
{
double rH = r / 255.0;
double gH = g / 255.0;
double bH = b / 255.0;
double Cmax = Math.Max(Math.Max(rH, gH), bH);
double Cmin = Math.Min(Math.Min(rH, gH), bH);
double delta = Cmax - Cmin;
L = (Cmin + Cmax) / 2.0;
if (delta == 0) S = 0;
else S = delta / (1.0 - Math.Abs(2.0 * L - 1));
if (delta == 0) H = 0;
else if (Cmax == rH) H = 60 * (((gH - bH) / delta) % 6);
else if (Cmax == gH) H = 60 * (((bH - rH) / delta) + 2);
else H = 60 * (((rH - gH) / delta) + 4);
}
}
}
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Drawing;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
...@@ -11,15 +6,63 @@ namespace S04_Projet ...@@ -11,15 +6,63 @@ namespace S04_Projet
{ {
class Program class Program
{ {
public static bool MULTITHREADING_LOAD = false; //public static bool MULTITHREADING_LOAD = false;
public static bool MULTITHREADING = false; public static bool MULTITHREADING = false;
public static int MAX_THREADS = Environment.ProcessorCount;
public static bool DEBUG = false; public static bool DEBUG = false;
public static MyImage img;
public static Stopwatch sw = new Stopwatch(); private static string[] menuItems = new string[] { "Charger une image", "Créer une image" };
private static Menu welcomeMenu = new Menu(menuItems);
private static MyImage img;
private static Stopwatch sw = new Stopwatch();
static void Main(string[] args) static void Main(string[] args)
{ {
Arguments(args, 0); #region Arguments
if (args.Length > 0)
{
Arguments(args, 0);
}
#endregion
#region Display
else
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("#############################################################");
Console.WriteLine("### Bienvenue ###");
Console.WriteLine("#############################################################");
Console.WriteLine();
int choice = welcomeMenu.ShowMenu(0, 5); // 0 pour charger une image, 1 pour la créer
if (choice == 0)
{
Console.Clear();
img = Display.LoadImage();
}
else
{
Console.Clear();
img = Display.CreateImageOperation();
Display.fileInfos = img.ToString();
}
Display.Operation ToDo = Display.AskForOperation();
MyImage newImg = Display.PerformOperation(ToDo, img);
while (newImg != null)
{
ToDo = Display.AskForOperation();
newImg = Display.PerformOperation(ToDo, newImg);
}
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Image sauvegardée en {0}ms!", Display.elapsedTime);
Console.Read();
}
#endregion
} }
private static void Arguments(string[] args, int i) private static void Arguments(string[] args, int i)
...@@ -45,7 +88,7 @@ namespace S04_Projet ...@@ -45,7 +88,7 @@ namespace S04_Projet
{ {
increment = 2; increment = 2;
int choix = 0; int choix = 0;
switch (args[i + 1].ToLower()) switch(args[i+1].ToLower())
{ {
case "flou": case "flou":
case "blur": case "blur":
...@@ -68,13 +111,18 @@ namespace S04_Projet ...@@ -68,13 +111,18 @@ namespace S04_Projet
choix = 0; choix = 0;
break; break;
} }
img = img.ApplyConvFilter(Filter.Filtres[choix], Filter.Facteurs[choix]); img = img.Filter(Filter.Filtres[choix], Filter.Facteurs[choix]);
}; }
else if (args[i] == "--threads" || args[i] == "-t")
{
MAX_THREADS = int.Parse(args[i + 1]);
increment = 2;
}
if (DEBUG) if (DEBUG)
{ {
sw.Stop(); sw.Stop();
File.AppendAllText("debug.csv", args[i].Replace("-", "") + ";" + sw.ElapsedMilliseconds + ";\n"); File.AppendAllText("debug.csv", args[i].Replace("-","") + ";" + sw.ElapsedMilliseconds + ";\n");
} }
if (i + increment < args.Length) Arguments(args, i + increment); if (i + increment < args.Length) Arguments(args, i + increment);
} }
......
@echo off
FOR /L %%i IN (1,1,100) DO (
ECHO %%i
ImageProcessing.exe -d -i img\flocon.bmp -f flou
)
\ No newline at end of file
@echo off
FOR /L %%i IN (1,1,100) DO (
ECHO %%i
ImageProcessing.exe -d -MT -i img\flocon.bmp -f flou
)
\ No newline at end of file
S04_Projet/bin/Debug/img/coco.bmp

188 KB | W: | H:

S04_Projet/bin/Debug/img/coco.bmp

188 KB | W: | H:

S04_Projet/bin/Debug/img/coco.bmp
S04_Projet/bin/Debug/img/coco.bmp
S04_Projet/bin/Debug/img/coco.bmp
S04_Projet/bin/Debug/img/coco.bmp
  • 2-up
  • Swipe
  • Onion skin
This image diff could not be displayed because it is too large. You can view the blob instead.
S04_Projet/bin/Debug/img/lena.bmp

768 KB | W: | H:

S04_Projet/bin/Debug/img/lena.bmp

369 KB | W: | H:

S04_Projet/bin/Debug/img/lena.bmp
S04_Projet/bin/Debug/img/lena.bmp
S04_Projet/bin/Debug/img/lena.bmp
S04_Projet/bin/Debug/img/lena.bmp
  • 2-up
  • Swipe
  • Onion skin
@echo off
FOR /L %%i IN (1,1,100) DO (
ECHO %%i
ImageProcessing.exe -d -i img\flocon.bmp
)
\ No newline at end of file
@echo off
FOR /L %%i IN (1,1,100) DO (
ECHO %%i
ImageProcessing.exe -d -MT -i img\flocon.bmp
)
\ No newline at end of file
@echo off
FOR /L %%i IN (1,1,100) DO (
ECHO %%i
ImageProcessing.exe -d -i img\flocon.bmp -o output.bmp
)
\ No newline at end of file
@echo off
FOR /L %%i IN (1,1,100) DO (
ECHO %%i
ImageProcessing.exe -d -MT -i img\flocon.bmp -o output.bmp
)
\ No newline at end of file
@echo off
FOR /L %%i IN (1,1,30) DO (
ECHO i : %%i
FOR /L %%j IN (1,1,20) DO (
ECHO j : %%j
ImageProcessing.exe -d -MT -t %%i -i img\flocon.bmp -f flou
)
)
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment