Код C# работает правильно и сохраняет изображение без проблем, но когда я использую версию C++, сохраненное изображение в оттенках серого выглядит как случайный шум. Вот мой основной код C#:
Код: Выделить всё
static void Main(string[] args)
{
DllLoader.LoadLibrary("ImageProcessingLib.dll");
double totalElapsedMicrosecondsCpp = 0;
double totalElapsedMicrosecondsCS = 0;
// Load your image
Bitmap bitmap = new Bitmap("nature.jpeg");
// Convert the image to byte array
byte[] rgbBytes = ConvertBitmapToByteArray(bitmap);
byte[] rgbBytesCpp = ConvertBitmapToByteArray(bitmap);
int runs = 2;
for (int i = 0; i < runs; i++)
{
Stopwatch sw = Stopwatch.StartNew();
// Call the P/Invoke function for C++ implementation
fixed (byte* ptr = rgbBytesCpp)
{
DllLoader.ConvertRgbToGrayscale(ptr, rgbBytesCpp.Length);
}
sw.Stop();
totalElapsedMicrosecondsCpp += sw.Elapsed.TotalMilliseconds * 1000;
}
for (int i = 0; i < runs; i++)
{
Stopwatch sw = Stopwatch.StartNew();
// C# grayscale function
ConvertRgbToGrayscale(rgbBytes);
sw.Stop();
totalElapsedMicrosecondsCS += sw.Elapsed.TotalMilliseconds * 1000;
}
double averageElapsedMicrosecondsPInvoke = totalElapsedMicrosecondsCpp / runs;
double averageElapsedMicrosecondsCSharp = totalElapsedMicrosecondsCS / runs;
Console.WriteLine("Average P/Invoke Grayscale Time: {0} microseconds", averageElapsedMicrosecondsPInvoke);
Console.WriteLine("Average Native C# Grayscale Time: {0} microseconds", averageElapsedMicrosecondsCSharp);
SaveGrayscaleImage(rgbBytesCpp, bitmap.Width, bitmap.Height, "Cpp.jpg");
SaveGrayscaleImage(rgbBytes, bitmap.Width, bitmap.Height, "C#.jpg");
Console.ReadLine();
}
public unsafe class DllLoader
{
// Static constructor to load the DLL without invoking any functions from it
static DllLoader()
{
LoadLibrary("ImageProcessingLib.dll");
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr LoadLibrary(string lpFileName);
// P/Invoke to call the C++ ConvertRgbToGrayscale function
[DllImport("ImageProcessingLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte* ConvertRgbToGrayscale(byte* pImage, int length);
}
Код: Выделить всё
#include
#include
extern "C" __declspec(dllexport) void ConvertRgbToGrayscaleSIMD(uint8_t* rgbArray, size_t length) {
// Ensure the array is aligned to 32-byte boundary (for AVX)
//__m256i* alignedArray = reinterpret_cast(_aligned_malloc(length, 32));
// Copy data to aligned memory
//memcpy(alignedArray, rgbArray, length);
// Grayscale coefficients approximated to integers: R = 0.3, G = 0.59, B = 0.11
const uint8_t coeffR = 77; // 0.3 * 256 ≈ 77
const uint8_t coeffG = 150; // 0.59 * 256 ≈ 150
const uint8_t coeffB = 29; // 0.11 * 256 ≈ 29
// Load the grayscale coefficients into AVX registers (broadcast to 8 elements)
__m256i coeff_r = _mm256_set1_epi8(coeffR);
__m256i coeff_g = _mm256_set1_epi8(coeffG);
__m256i coeff_b = _mm256_set1_epi8(coeffB);
size_t i = 0;
// Process 8 pixels (24 bytes) at once
for (; i + 23 < length; i += 24) { // 8 pixels (24 bytes) per loop
// Load 24 bytes (8 pixels, RGBRGBRGB...)
__m256i rgb1 = _mm256_loadu_si256(reinterpret_cast(rgbArray + i));
// Extract the R, G, B channels
__m256i r = _mm256_and_si256(rgb1, _mm256_set1_epi8(0xFF)); // R channel (bytes 0, 3, 6, 9, 12, 15, 18, 21)
__m256i g = _mm256_and_si256(_mm256_srli_epi32(rgb1, 8), _mm256_set1_epi8(0xFF)); // G channel (bytes 1, 4, 7, 10, 13, 16, 19, 22)
__m256i b = _mm256_and_si256(_mm256_srli_epi32(rgb1, 16), _mm256_set1_epi8(0xFF)); // B channel (bytes 2, 5, 8, 11, 14, 17, 20, 23)
// Calculate grayscale
__m256i gray_r = _mm256_mullo_epi16(r, coeff_r); // R * coeffR
__m256i gray_g = _mm256_mullo_epi16(g, coeff_g); // G * coeffG
__m256i gray_b = _mm256_mullo_epi16(b, coeff_b); // B * coeffB
// Add the values (R * coeffR + G * coeffG + B * coeffB)
__m256i gray = _mm256_add_epi8(
_mm256_add_epi8(gray_r, gray_g),
gray_b
);
// Right shift by 8 to normalize the grayscale values
gray = _mm256_srli_epi16(gray, 8);
// Duplicate grayscale values to R, G, B channels
__m256i gray_rgb = _mm256_packus_epi16(gray, gray);
// Store the resulting grayscale values back into the rgbArray
_mm256_storeu_si256(reinterpret_cast(rgbArray + i), gray_rgb);
}
// Handle any leftover pixels that don't fit into full 8-pixel chunks
for (; i + 2 < length; i += 3) {
uint8_t r = rgbArray[i];
uint8_t g = rgbArray[i + 1];
uint8_t b = rgbArray[i + 2];
uint8_t gray = static_cast((coeffR * r + coeffG * g + coeffB * b) >> 8);
rgbArray[i] = gray;
rgbArray[i + 1] = gray;
rgbArray[i + 2] = gray;
}
// Handle any leftover pixels that don't fit into full RGB triplets (i.e., length % 3 != 0)
size_t remainder = length % 3;
if (remainder > 0) {
for (size_t j = length - remainder; j < length; ++j) {
rgbArray[j] = rgbArray[j]; // No change
}
}
//memcpy(rgbArray, alignedArray, length);
//_aligned_free(alignedArray);
}
Я использую .net framework 4.8 и мои текущие результаты производительности:
Я использую .net framework 4.8 и мои текущие результаты производительности:
Я использую .net framework 4.8 p>
Преобразование изображения 4K из RGB в оттенки серого
C#: 18 мс (рабочее)
C++ P/Invoke Non SIMD : 13 мс (рабочая)
C++ P/Invoke SIMD: 7 мс (проблема со случайным шумом)
Вопрос: есть ли способ выполнить SIMD преобразование оттенков серого в этом байте [] без необходимости выравнивания памяти? Или есть другой эффективный способ справиться с этой проблемой, позволяющий избежать проблем с шумом и при этом сохранить производительность?
Подробнее здесь: https://stackoverflow.com/questions/791 ... ment-issue
Мобильная версия