Răsfoiți Sursa

Image convolution function ImageKernelConvolution (#3528)

* Added image convultion ImageKernelConvolution

* comment changes

* spelling changes and change to kernel size

* removed kernel normalization inside function

* fix to formating
Karim 1 an în urmă
părinte
comite
21469e92b0

+ 1 - 0
examples/Makefile

@@ -490,6 +490,7 @@ TEXTURES = \
     textures/textures_gif_player \
     textures/textures_image_drawing \
     textures/textures_image_generation \
+    textures/textures_image_kernel \
     textures/textures_image_loading \
     textures/textures_image_processing \
     textures/textures_image_rotate \

+ 127 - 0
examples/textures/textures_image_kernel.c

@@ -0,0 +1,127 @@
+/*******************************************************************************************
+*
+*   raylib [textures] example - Image loading and texture creation
+*
+*   NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
+*
+*   Example originally created with raylib 1.3, last time updated with raylib 1.3
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2015-2023 Karim Salem (@kimo-s)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+void normalizeKernel(float *kernel, int size){
+    float sum = 0.0f;
+    for(int i = 0; i < size; i++)
+    {
+        sum += kernel[i]; 
+    }
+
+    if(sum != 0.0f)
+    {
+        for(int i = 0; i < size; i++)
+        {
+            kernel[i] /= sum; 
+        }
+    }
+}
+
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+
+    Image image = LoadImage("resources/cat.png");     // Loaded in CPU memory (RAM)
+
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - image convolution");
+
+    float gaussiankernel[] = {1.0, 2.0, 1.0,
+                    2.0, 4.0, 2.0,
+                    1.0, 2.0, 1.0};
+
+    float sobelkernel[] = {1.0, 0.0, -1.0,
+                    2.0, 0.0, -2.0,
+                    1.0, 0.0, -1.0};
+
+    float sharpenkernel[] = {0.0, -1.0, 0.0,
+                        -1.0, 5.0, -1.0,
+                        0.0, -1.0, 0.0};
+
+    normalizeKernel(gaussiankernel, 9);
+    normalizeKernel(sharpenkernel, 9);
+    normalizeKernel(sobelkernel, 9);
+
+    Image catSharpend = ImageCopy(image);
+    ImageKernelConvolution(&catSharpend, sharpenkernel, 9);
+ 
+    Image catSobel = ImageCopy(image);
+    ImageKernelConvolution(&catSobel, sobelkernel, 9);
+
+    Image catGaussian = ImageCopy(image);
+    for(int i = 0; i < 6; i++)
+    {
+        ImageKernelConvolution(&catGaussian, gaussiankernel, 9);
+    }
+
+    ImageCrop(&image, (Rectangle){ 0, 0, (float)200, (float)450 });
+    ImageCrop(&catGaussian, (Rectangle){ 0, 0, (float)200, (float)450 });
+    ImageCrop(&catSobel, (Rectangle){ 0, 0, (float)200, (float)450 });
+    ImageCrop(&catSharpend, (Rectangle){ 0, 0, (float)200, (float)450 });
+    Texture2D texture = LoadTextureFromImage(image);          // Image converted to texture, GPU memory (VRAM)
+    Texture2D catSharpendTexture = LoadTextureFromImage(catSharpend);
+    Texture2D catSobelTexture = LoadTextureFromImage(catSobel);
+    Texture2D catGaussianTexture = LoadTextureFromImage(catGaussian);
+    UnloadImage(image);   // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM
+    UnloadImage(catGaussian);
+    UnloadImage(catSobel);
+    UnloadImage(catSharpend);
+
+    SetTargetFPS(60);     // Set our game to run at 60 frames-per-second
+    //---------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // TODO: Update your variables here
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawTexture(catSharpendTexture, 0, 0, WHITE);
+            DrawTexture(catSobelTexture, 200, 0, WHITE);
+            DrawTexture(catGaussianTexture, 400, 0, WHITE);
+            DrawTexture(texture, 600, 0, WHITE);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(texture);       // Texture unloading
+    UnloadTexture(catGaussianTexture);
+    UnloadTexture(catSobelTexture);
+    UnloadTexture(catSharpendTexture);
+
+    CloseWindow();                // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}

+ 1 - 0
projects/Notepad++/raylib_npp_parser/raylib_to_parse.h

@@ -375,6 +375,7 @@ RLAPI void ImageAlphaClear(Image *image, Color color, float threshold);
 RLAPI void ImageAlphaMask(Image *image, Image alphaMask);                                                // Apply alpha mask to image
 RLAPI void ImageAlphaPremultiply(Image *image);                                                          // Premultiply alpha channel
 RLAPI void ImageBlurGaussian(Image *image, int blurSize);                                                // Apply Gaussian blur using a box blur approximation
+RLAPI void ImageKernelConvolution(Image *image, float* kernel, int kernelSize);                         // Apply Custom Square image convolution kernel
 RLAPI void ImageResize(Image *image, int newWidth, int newHeight);                                       // Resize image (Bicubic scaling algorithm)
 RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight);                                      // Resize image (Nearest-Neighbor scaling algorithm)
 RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill);  // Resize canvas and fill with color

+ 1 - 0
src/raylib.h

@@ -1329,6 +1329,7 @@ RLAPI void ImageAlphaClear(Image *image, Color color, float threshold);
 RLAPI void ImageAlphaMask(Image *image, Image alphaMask);                                                // Apply alpha mask to image
 RLAPI void ImageAlphaPremultiply(Image *image);                                                          // Premultiply alpha channel
 RLAPI void ImageBlurGaussian(Image *image, int blurSize);                                                // Apply Gaussian blur using a box blur approximation
+RLAPI void ImageKernelConvolution(Image *image, float* kernel, int kernelSize);                         // Apply Custom Square image convolution kernel
 RLAPI void ImageResize(Image *image, int newWidth, int newHeight);                                       // Resize image (Bicubic scaling algorithm)
 RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight);                                      // Resize image (Nearest-Neighbor scaling algorithm)
 RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill);  // Resize canvas and fill with color

+ 142 - 0
src/rtextures.c

@@ -2082,6 +2082,148 @@ void ImageBlurGaussian(Image *image, int blurSize) {
     ImageFormat(image, format);
 }
 
+// The kernel matrix is assumed to be square. Only supply the width of the kernel.
+void ImageKernelConvolution(Image *image, float* kernel, int kernelSize){
+
+    if ((image->data == NULL) || (image->width == 0) || (image->height == 0) || kernel == NULL) return;
+
+    int kernelWidth = (int)sqrtf((float)kernelSize);
+    if (kernelWidth*kernelWidth != kernelSize)
+    {
+        TRACELOG(LOG_WARNING, "IMAGE: Convolution kernel must be square to be applied");
+        return;
+    }
+
+    Color *pixels = LoadImageColors(*image);
+
+    Vector4 *imageCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
+    Vector4 *temp = RL_MALLOC(kernelSize*sizeof(Vector4));
+
+
+    for(int i = 0; i < kernelSize; i++){
+        temp[i].x = 0.0f;
+        temp[i].y = 0.0f;
+        temp[i].z = 0.0f;
+        temp[i].w = 0.0f;
+    }
+
+    float rRes = 0.0f;
+    float gRes = 0.0f;
+    float bRes = 0.0f;
+    float aRes = 0.0f;
+
+
+    int startRange, endRange;
+    if(kernelWidth % 2 == 0)
+    {
+        startRange = -kernelWidth/2;
+        endRange = kernelWidth/2;
+    } else 
+    {
+        startRange = -kernelWidth/2;
+        endRange = kernelWidth/2+1;
+    }
+    for(int x = 0; x < image->height; x++) 
+    {
+        for(int y = 0; y < image->width; y++) 
+        {
+
+            for(int xk = startRange; xk < endRange; xk++)
+            {
+                for(int yk = startRange; yk < endRange; yk++)
+                {
+                    int xkabs = xk + kernelWidth/2;
+                    int ykabs = yk + kernelWidth/2;
+                    size_t imgindex = image->width * (x+xk) + (y+yk);
+                    if(imgindex < 0 || imgindex >= image->width * image->height){
+                        temp[kernelWidth * xkabs + ykabs].x = 0.0f;
+                        temp[kernelWidth * xkabs + ykabs].y = 0.0f;
+                        temp[kernelWidth * xkabs + ykabs].z = 0.0f;
+                        temp[kernelWidth * xkabs + ykabs].w = 0.0f;
+                    } else {
+                        temp[kernelWidth * xkabs + ykabs].x = ((float)pixels[imgindex].r)/255.0f * kernel[kernelWidth * xkabs + ykabs];
+                        temp[kernelWidth * xkabs + ykabs].y = ((float)pixels[imgindex].g)/255.0f * kernel[kernelWidth * xkabs + ykabs];
+                        temp[kernelWidth * xkabs + ykabs].z = ((float)pixels[imgindex].b)/255.0f * kernel[kernelWidth * xkabs + ykabs];
+                        temp[kernelWidth * xkabs + ykabs].w = ((float)pixels[imgindex].a)/255.0f * kernel[kernelWidth * xkabs + ykabs];
+                    }
+                }
+            }
+
+            for(int i = 0; i < kernelSize; i++)
+            {
+                rRes += temp[i].x;
+                gRes += temp[i].y;
+                bRes += temp[i].z;
+                aRes += temp[i].w;
+            }
+
+            if(rRes < 0.0f)
+            {
+                rRes = 0.0f;
+            }
+            if(gRes < 0.0f)
+            {
+                gRes = 0.0f;
+            }
+            if(bRes < 0.0f)
+            {
+                bRes = 0.0f;
+            }
+
+            if(rRes > 1.0f)
+            {
+                rRes = 1.0f;
+            }
+            if(gRes > 1.0f)
+            {
+                gRes = 1.0f;
+            }
+             if(bRes > 1.0f)
+            {
+                bRes = 1.0f;
+            }
+
+            imageCopy2[image->width * (x) + (y)].x = rRes;
+            imageCopy2[image->width * (x) + (y)].y = gRes;
+            imageCopy2[image->width * (x) + (y)].z = bRes;
+            imageCopy2[image->width * (x) + (y)].w = aRes;
+
+            rRes = 0.0f;
+            gRes = 0.0f;
+            bRes = 0.0f;
+            aRes = 0.0f;
+
+            for(int i = 0; i < kernelSize; i++)
+            {
+                temp[i].x = 0.0f;
+                temp[i].y = 0.0f;
+                temp[i].z = 0.0f;
+                temp[i].w = 0.0f;
+            }
+        }
+    }
+
+    for (int i = 0; i < (image->width) * (image->height); i++) 
+    {
+        float alpha = (float)imageCopy2[i].w;
+        pixels[i].r = (unsigned char)((imageCopy2[i].x)*255.0f);
+        pixels[i].g = (unsigned char)((imageCopy2[i].y)*255.0f);
+        pixels[i].b = (unsigned char)((imageCopy2[i].z)*255.0f);
+        pixels[i].a = (unsigned char)((alpha)*255.0f);
+        // printf("pixels[%d] = %d", i, pixels[i].r); 
+    }
+
+
+    int format = image->format;
+    RL_FREE(image->data);
+    RL_FREE(imageCopy2);
+    RL_FREE(temp);
+
+    image->data = pixels;
+    image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+    ImageFormat(image, format);
+}
+
 // Generate all mipmap levels for a provided image
 // NOTE 1: Supports POT and NPOT images
 // NOTE 2: image.data is scaled to include mipmap levels