Skip to content

Commit 31fd480

Browse files
committed
add files
1 parent 3a5ab4a commit 31fd480

4 files changed

Lines changed: 378 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System;
2+
using System.IO;
3+
4+
using ImageConvolution;
5+
6+
using Xunit;
7+
8+
namespace ImageConvolution.Tests;
9+
10+
public class ConvolutionTests
11+
{
12+
[Fact]
13+
public void Test_ImageIO_SaveAndLoad()
14+
{
15+
string testPath = "test_image.png";
16+
double[,] originalData = new double[2, 2] { { 0, 255 }, { 128, 64 } };
17+
18+
try
19+
{
20+
ImageIO.SaveImage(originalData, testPath);
21+
Assert.True(File.Exists(testPath));
22+
23+
double[,] loadedData = ImageIO.LoadAsGrayscale(testPath);
24+
25+
Assert.Equal(2, loadedData.GetLength(0));
26+
Assert.Equal(2, loadedData.GetLength(1));
27+
}
28+
finally
29+
{
30+
if (File.Exists(testPath)) File.Delete(testPath);
31+
}
32+
}
33+
34+
[Fact]
35+
public void Test_Convolve_ExtendStrategy()
36+
{
37+
double[,] image = { { 10, 10, 10 }, { 10, 10, 10 }, { 10, 10, 10 } };
38+
double[,] result = ConvolutionProcessor.Convolve(image, Kernels.BlurBox, ConvolutionProcessor.EdgeStrategy.Extend);
39+
Assert.Equal(10, Math.Round(result[1, 1]));
40+
}
41+
42+
[Fact]
43+
public void Test_Convolve_ZeroPaddingStrategy()
44+
{
45+
double[,] image = { { 10, 10, 10 }, { 10, 10, 10 }, { 10, 10, 10 } };
46+
double[,] result = ConvolutionProcessor.Convolve(image, Kernels.BlurBox, ConvolutionProcessor.EdgeStrategy.ZeroPadding);
47+
Assert.True(result[0, 0] < 10);
48+
}
49+
50+
[Fact]
51+
public void Test_ConvolveParallel()
52+
{
53+
double[,] image = { { 100, 100, 100 }, { 100, 100, 100 }, { 100, 100, 100 } };
54+
double[,] result = ParallelConvolutionProcessor.ConvolveParallel(image, Kernels.BlurBox, ParallelConvolutionProcessor.EdgeStrategy.Extend);
55+
Assert.Equal(100, Math.Round(result[1, 1]));
56+
}
57+
58+
[Fact]
59+
public void Test_KernelsExist()
60+
{
61+
Assert.NotNull(Kernels.BlurBox);
62+
Assert.NotNull(Kernels.Sharpen);
63+
Assert.NotNull(Kernels.Laplacian);
64+
}
65+
66+
[Fact]
67+
public void Test_ConvolveParallel_ZeroPadding()
68+
{
69+
double[,] image = {
70+
{ 10, 10, 10 },
71+
{ 10, 10, 10 },
72+
{ 10, 10, 10 }
73+
};
74+
75+
double[,] result = ParallelConvolutionProcessor.ConvolveParallel(
76+
image,
77+
Kernels.BlurBox,
78+
ParallelConvolutionProcessor.EdgeStrategy.ZeroPadding);
79+
80+
Assert.True(result[0, 0] < 10);
81+
}
82+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<IsPackable>false</IsPackable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="coverlet.collector" Version="6.0.2" />
12+
<PackageReference Include="coverlet.msbuild" Version="8.0.1">
13+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
14+
<PrivateAssets>all</PrivateAssets>
15+
</PackageReference>
16+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
17+
<PackageReference Include="xunit" Version="2.9.2" />
18+
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<Using Include="Xunit" />
23+
</ItemGroup>
24+
25+
<ItemGroup>
26+
<ProjectReference Include="..\ImageConvolution\ImageConvolution.csproj" />
27+
</ItemGroup>
28+
29+
</Project>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net9.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
12+
</ItemGroup>
13+
14+
</Project>

‎ImageConvolution/Program.cs‎

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
using System;
2+
using System.Data;
3+
using System.Diagnostics;
4+
using System.Diagnostics.CodeAnalysis;
5+
using System.IO;
6+
using System.Threading.Tasks;
7+
8+
using SixLabors.ImageSharp;
9+
using SixLabors.ImageSharp.PixelFormats;
10+
11+
namespace ImageConvolution
12+
{
13+
[ExcludeFromCodeCoverage]
14+
class Program
15+
{
16+
static void Main(string[] args)
17+
{
18+
Console.WriteLine("=== Программа свёртки изображений ===");
19+
Console.Write("Введите путь к изображению (перетащите файл в консоль): ");
20+
21+
string? inputPath = Console.ReadLine()?.Trim('\"', ' ', '\'');
22+
23+
if (string.IsNullOrEmpty(inputPath) || !File.Exists(inputPath))
24+
{
25+
Console.WriteLine("Ошибка: Файл не найден! Проверьте путь и попробуйте снова.");
26+
return;
27+
}
28+
29+
string directory = Path.GetDirectoryName(inputPath) ?? "";
30+
string fileName = Path.GetFileNameWithoutExtension(inputPath);
31+
string extension = Path.GetExtension(inputPath);
32+
string outputPath = Path.Combine(directory, fileName + "_filtered" + extension);
33+
34+
try
35+
{
36+
Console.WriteLine("\n1. Загрузка изображения...");
37+
double[,] image = ImageIO.LoadAsGrayscale(inputPath);
38+
39+
Console.WriteLine("2. Применение свёртки (Размытие / Blur)...");
40+
Stopwatch sw = Stopwatch.StartNew();
41+
42+
double[,] result = ConvolutionProcessor.Convolve(
43+
image,
44+
Kernels.BlurBox,
45+
ConvolutionProcessor.EdgeStrategy.Extend);
46+
47+
sw.Stop();
48+
Console.WriteLine("2.1. Применение параллельной свёртки (Размытие / Blur)...");
49+
Stopwatch sw1 = Stopwatch.StartNew();
50+
51+
double[,] result2 = ParallelConvolutionProcessor.ConvolveParallel(
52+
image,
53+
Kernels.BlurBox,
54+
ParallelConvolutionProcessor.EdgeStrategy.Extend);
55+
56+
sw1.Stop();
57+
58+
Console.WriteLine($"Свёртка завершена за {sw.ElapsedMilliseconds} мс");
59+
60+
Console.WriteLine($"Параллельная свёртка завершена за {sw1.ElapsedMilliseconds} мс");
61+
62+
Console.WriteLine("3. Сохранение результата...");
63+
ImageIO.SaveImage(result, outputPath);
64+
65+
Console.WriteLine($"\nГотово! Изображение сохранено здесь:\n{outputPath}");
66+
}
67+
catch (Exception ex)
68+
{
69+
Console.WriteLine($"Произошла ошибка: {ex.Message}");
70+
}
71+
}
72+
}
73+
74+
public class ImageIO
75+
{
76+
public static double[,] LoadAsGrayscale(string path)
77+
{
78+
using Image<Rgba32> image = Image.Load<Rgba32>(path);
79+
int width = image.Width;
80+
int height = image.Height;
81+
double[,] result = new double[height, width];
82+
83+
for (int y = 0; y < height; y++)
84+
{
85+
for (int x = 0; x < width; x++)
86+
{
87+
Rgba32 pixel = image[x, y];
88+
double gray = 0.299 * pixel.R + 0.587 * pixel.G + 0.114 * pixel.B;
89+
result[y, x] = gray;
90+
}
91+
}
92+
return result;
93+
}
94+
public static void SaveImage(double[,] data, string path)
95+
{
96+
int height = data.GetLength(0);
97+
int width = data.GetLength(1);
98+
99+
using Image<Rgba32> image = new Image<Rgba32>(width, height);
100+
for (int y = 0; y < height; y++)
101+
{
102+
for (int x = 0; x < width; x++)
103+
{
104+
double val = data[y, x];
105+
byte gray = (byte)Math.Clamp(val, 0, 255);
106+
image[x, y] = new Rgba32(gray, gray, gray);
107+
}
108+
}
109+
image.Save(path);
110+
}
111+
}
112+
113+
public class Kernels
114+
{
115+
public static double[,] Sharpen = new double[,]
116+
{
117+
{ 0, -1, 0},
118+
{ -1, 5, -1},
119+
{ 0, -1, 0}
120+
};
121+
public static double[,] Laplacian = new double[,]
122+
{
123+
{-1, -1, -1},
124+
{-1, 8, -1},
125+
{-1, -1, -1}
126+
};
127+
public static double[,] BlurBox = new double[,]
128+
{
129+
{1.0/9.0, 1.0/9.0, 1.0/9.0},
130+
{1.0/9.0, 1.0/9.0, 1.0/9.0},
131+
{1.0/9.0, 1.0/9.0, 1.0/9.0}
132+
};
133+
}
134+
135+
public class ConvolutionProcessor
136+
{
137+
public enum EdgeStrategy
138+
{
139+
Extend,
140+
ZeroPadding
141+
}
142+
143+
public static double[,] Convolve(double[,] image, double[,] kernel, EdgeStrategy strategy = EdgeStrategy.Extend)
144+
{
145+
int imgheight = image.GetLength(0);
146+
int imgwidth = image.GetLength(1);
147+
int kheight = kernel.GetLength(0);
148+
int kwidth = kernel.GetLength(1);
149+
150+
int offsetY = kheight / 2;
151+
int offsetX = kwidth / 2;
152+
153+
double[,] result = new double[imgheight, imgwidth];
154+
155+
for (int y = 0; y < imgheight; y++)
156+
{
157+
for (int x = 0; x < imgwidth; x++)
158+
{
159+
double sum = 0.0;
160+
161+
for (int ky = 0; ky < kheight; ky++)
162+
{
163+
for (int kx = 0; kx < kwidth; kx++)
164+
{
165+
int pixelY = y + ky - offsetY;
166+
int pixelX = x + kx - offsetX;
167+
double pixelValue = 0.0;
168+
169+
if (strategy == EdgeStrategy.Extend)
170+
{
171+
if (pixelY < 0) pixelY = 0;
172+
if (pixelY >= imgheight) pixelY = imgheight - 1;
173+
if (pixelX < 0) pixelX = 0;
174+
if (pixelX >= imgwidth) pixelX = imgwidth - 1;
175+
176+
pixelValue = image[pixelY, pixelX];
177+
}
178+
else if (strategy == EdgeStrategy.ZeroPadding)
179+
{
180+
if (pixelY >= 0 && pixelY < imgheight && pixelX >= 0 && pixelX < imgwidth)
181+
{
182+
pixelValue = image[pixelY, pixelX];
183+
}
184+
}
185+
sum += pixelValue * kernel[ky, kx];
186+
}
187+
}
188+
189+
result[y, x] = sum;
190+
}
191+
}
192+
return result;
193+
}
194+
}
195+
public class ParallelConvolutionProcessor
196+
{
197+
public enum EdgeStrategy
198+
{
199+
Extend,
200+
ZeroPadding
201+
}
202+
203+
public static double[,] ConvolveParallel(double[,] image, double[,] kernal, EdgeStrategy strategy = EdgeStrategy.Extend)
204+
{
205+
int imgwidth = image.GetLength(1);
206+
int imgheight = image.GetLength(0);
207+
int kwidth = kernal.GetLength(0);
208+
int kheight = kernal.GetLength(1);
209+
210+
int offsetX = kwidth / 2;
211+
int offsetY = kheight / 2;
212+
213+
double[,] result2 = new double[imgheight, imgwidth];
214+
Parallel.For(0, imgheight, y =>
215+
{
216+
for (int x = 0; x < imgwidth; x++)
217+
{
218+
double sum = 0.0;
219+
220+
for (int ky = 0; ky < kheight; ky++)
221+
{
222+
for (int kx = 0; kx < kwidth; kx++)
223+
{
224+
int pixelY = y + ky - offsetY;
225+
int pixelX = x + kx - offsetX;
226+
double pixelValue = 0.0;
227+
228+
if (strategy == EdgeStrategy.Extend)
229+
{
230+
if (pixelY < 0) pixelY = 0;
231+
if (pixelY >= imgheight) pixelY = imgheight - 1;
232+
if (pixelX < 0) pixelX = 0;
233+
if (pixelX >= imgwidth) pixelX = imgwidth - 1;
234+
235+
pixelValue = image[pixelY, pixelX];
236+
}
237+
else if (strategy == EdgeStrategy.ZeroPadding)
238+
{
239+
if (pixelY >= 0 && pixelY < imgheight && pixelX >= 0 && pixelX < imgwidth)
240+
{
241+
pixelValue = image[pixelY, pixelX];
242+
}
243+
}
244+
sum += pixelValue * kernal[ky, kx];
245+
}
246+
}
247+
result2[y, x] = sum;
248+
}
249+
});
250+
return result2;
251+
}
252+
}
253+
}

0 commit comments

Comments
 (0)