Skip to content

Commit

Permalink
Add function to convert image to anti-aliased black & white
Browse files Browse the repository at this point in the history
  • Loading branch information
ad-si committed Jan 11, 2025
1 parent 0c3d013 commit ee136b9
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 1 deletion.
62 changes: 62 additions & 0 deletions cbits/simplecv.c
Original file line number Diff line number Diff line change
Expand Up @@ -453,3 +453,65 @@ unsigned char const * const apply_gaussian_blur(

return blurred_data;
}


/**
* Convert image to anti-aliased black and white.
* 1. Convert the image to grayscale.
* 2. Subtract blurred image from the original image to get the high frequencies.
* 3. Apply OTSU's threshold to get the optimal threshold.
* 4. Apply the threshold + offset to get the anti-aliased image.
*
* @param width Width of the image.
* @param height Height of the image.
* @param data Pointer to the pixel data.
* @return Pointer to the blurred image data.
*/
unsigned char const * const bw_smooth_smart(
unsigned int width,
unsigned int height,
unsigned char const * const data
) {
unsigned char const * const grayscale_data = grayscale(width, height, data);

// Calculate blur radius dependent on image size
// (Empirical formula after testing)
double blurRadius = (sqrt((double)width * (double)height)) * 0.1;

unsigned char const * const blurred_data = apply_gaussian_blur(
width,
height,
blurRadius,
grayscale_data
);

unsigned int img_length_px = width * height;
unsigned char *high_freq_data = malloc(img_length_px * 4);

if (!high_freq_data) { // Memory allocation failed
free((void *)grayscale_data);
free((void *)blurred_data);
return NULL;
}

// Subtract blurred image from the original image to get the high frequencies
// and invert the high frequencies to get a white background.
for (unsigned int i = 0; i < img_length_px; i++) {
unsigned int rgba_idx = i * 4;
int high_freq_val = 127 + grayscale_data[rgba_idx] - blurred_data[rgba_idx];
high_freq_data[rgba_idx] = high_freq_val; // R
high_freq_data[rgba_idx + 1] = high_freq_val; // G
high_freq_data[rgba_idx + 2] = high_freq_val; // B
high_freq_data[rgba_idx + 3] = 255; // A
}

free((void *)grayscale_data);
free((void *)blurred_data);

unsigned char const * const final_data = otsu_threshold_rgba(
width, height, true, high_freq_data
);

free(high_freq_data);
return final_data;
}
6 changes: 6 additions & 0 deletions cbits/simplecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ unsigned char const * const otsu_threshold_rgba(
bool use_double_threshold,
unsigned char const * const data
);

unsigned char const * const bw_smooth_smart(
unsigned int width,
unsigned int height,
unsigned char const * const data
);
2 changes: 1 addition & 1 deletion source/Lib.hs
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ correctAndWrite transformBackend inPath outPath ((bl, _), (tl, _), (tr, _), (br,
savePngImage outPath (ImageRGBA8 bwImg)
--
BlackWhiteSmoothExport -> do
bwImgPtr <- SCV.otsu_threshold_rgba width height True resutlImg
bwImgPtr <- SCV.bwSmoothSmart width height resutlImg
bwImgForeignPtr <- newForeignPtr_ (castPtr bwImgPtr)
let bwImg = imageFromUnsafePtr width height bwImgForeignPtr
savePngImage outPath (ImageRGBA8 bwImg)
Expand Down
7 changes: 7 additions & 0 deletions source/SimpleCV.chs
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,10 @@ instance Storable Matrix3x3 where
, castPtr `Ptr Matrix3x3' -- ^ Transformation matrix
} -> `Ptr CUChar' castPtr -- ^ Transformed image data
#}

{#fun bw_smooth_smart as ^
{ `Int' -- ^ width
, `Int' -- ^ height
, identity `Ptr CUChar' -- ^ Original image data
} -> `Ptr CUChar' castPtr -- ^ Anti-aliased black and white image data
#}

0 comments on commit ee136b9

Please sign in to comment.