Skip to content

Commit

Permalink
Add new BlackWhiteSmooth export option
Browse files Browse the repository at this point in the history
  • Loading branch information
ad-si committed Jan 11, 2025
1 parent 7db1e97 commit 20e7734
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 13 deletions.
4 changes: 2 additions & 2 deletions app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
module Main where

import Protolude (
Bool (True),
Bool (True, False),
Char,
Either (Left, Right),
IO,
Expand Down Expand Up @@ -97,7 +97,7 @@ execWithArgs config cliArgs = do
height = P.snd bitmapData.bitmapSize
withForeignPtr (castForeignPtr bitmapData.bitmapPointer) $ \ptr -> do
-- resutlImg <- grayscale width height ptr
resutlImg <- otsu_threshold_rgba width height ptr
resutlImg <- otsu_threshold_rgba width height False ptr
resultImgForeignPtr <- newForeignPtr_ (castPtr resutlImg)
let grayscalePicture =
bitmapOfForeignPtr
Expand Down
51 changes: 50 additions & 1 deletion cbits/simplecv.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,37 @@ void apply_global_threshold(
}


/**
* Applies two thresholds to the image data by blackening pixels
* below the lower threshold and whitening pixels above the upper threshold.
* Pixels between the two thresholds are scaled to the range [0, 255].
*
* @param img_length_px Length of the image data in pixels.
* @param data Pointer to the image data.
* @param lower_threshold Every pixel below this value will be blackened.
* @param upper_threshold Every pixel above this value will be whitened.
*
*/
void apply_double_threshold(
unsigned int img_length_px,
unsigned char *data,
unsigned char lower_threshold,
unsigned char upper_threshold
) {
for (unsigned int i = 0; i < img_length_px; i++) {
if (data[i] < lower_threshold) {
data[i] = 0;
}
else if (data[i] > upper_threshold) {
data[i] = 255;
}
else {
data[i] = (data[i] - lower_threshold) * 255 / (upper_threshold - lower_threshold);
}
}
}


/**
* Convert single channel grayscale image data to
* RGBA row-major top-to-bottom image data.
Expand Down Expand Up @@ -254,12 +285,14 @@ unsigned char const * const single_to_multichannel(
*
* @param width Width of the image.
* @param height Height of the image.
* @param use_double_threshold Use double thresholding.
* @param data Pointer to the pixel data.
* @return Pointer to the monochrome image data.
*/
unsigned char const * const otsu_threshold_rgba(
unsigned int width,
unsigned int height,
bool use_double_threshold,
unsigned char const * const data
) {
unsigned char *grayscale_img = rgba_to_grayscale(width, height, data);
Expand Down Expand Up @@ -306,7 +339,23 @@ unsigned char const * const otsu_threshold_rgba(
}
}

apply_global_threshold(img_length_px, grayscale_img, optimal_threshold);
const int threshold_range_offset = 16;

if (use_double_threshold) {
apply_double_threshold(
img_length_px,
grayscale_img,
optimal_threshold - threshold_range_offset,
optimal_threshold + threshold_range_offset
);
}
else {
apply_global_threshold(
img_length_px,
grayscale_img,
optimal_threshold
);
}

unsigned char const * const monochrome_data = single_to_multichannel(
width,
Expand Down
3 changes: 3 additions & 0 deletions cbits/simplecv.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <stdbool.h>

unsigned char const * const grayscale(
unsigned int width,
unsigned int height,
Expand All @@ -21,5 +23,6 @@ void apply_global_threshold(
unsigned char const * const otsu_threshold_rgba(
unsigned int width,
unsigned int height,
bool use_double_threshold,
unsigned char const * const data
);
2 changes: 1 addition & 1 deletion cbits/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ int test_otsu_threshold () {
0,0,0,255, 2,2,2,255, 9,9,9,255, 8,8,8,255
};

unsigned char const * const monochrome_data = otsu_threshold_rgba(width, height, data);
unsigned char const * const monochrome_data = otsu_threshold_rgba(width, height, false, data);

unsigned char expected_data[64] = {
0,0,0,255, 0,0,0,255, 255,255,255,255, 255,255,255,255,
Expand Down
Binary file modified images/words.afdesign
Binary file not shown.
Binary file modified images/words.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 13 additions & 1 deletion source/Lib.hs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,8 @@ handleImageViewEvent event appState =
submitSelection appState GrayscaleExport
Just (Button{text = "Save BW"}) ->
submitSelection appState BlackWhiteExport
Just (Button{text = "Save BW Smooth"}) ->
submitSelection appState BlackWhiteSmoothExport
_ -> do
let
point = appCoordToImgCoord appState clickedPoint
Expand Down Expand Up @@ -709,6 +711,9 @@ getConvertArgs inPath outPath projMap shape exportMode =
UnmodifiedExport -> []
GrayscaleExport -> ["-colorspace", "gray", "-normalize"]
BlackWhiteExport -> ["-auto-threshold", "OTSU", "-monochrome"]
-- TODO: Use the correct algorithm as seen in
-- https://github.com/ad-si/dotfiles/blob/master/bin/level
BlackWhiteSmoothExport -> ["-auto-threshold", "OTSU", "-monochrome"]
<> [ "+repage"
, fixOutputPath exportMode outPath
]
Expand Down Expand Up @@ -831,6 +836,7 @@ correctAndWrite transformBackend inPath outPath ((bl, _), (tl, _), (tr, _), (br,
UnmodifiedExport -> writeImage outPath corrected
GrayscaleExport -> pure ()
BlackWhiteExport -> pure ()
BlackWhiteSmoothExport -> pure ()
--
SimpleCVBackend -> do
P.putText "ℹ️ Use SimpleCV backend"
Expand Down Expand Up @@ -923,7 +929,13 @@ correctAndWrite transformBackend inPath outPath ((bl, _), (tl, _), (tr, _), (br,
savePngImage outPath (ImageRGBA8 grayImg)
--
BlackWhiteExport -> do
bwImgPtr <- SCV.otsu_threshold_rgba width height resutlImg
bwImgPtr <- SCV.otsu_threshold_rgba width height False resutlImg
bwImgForeignPtr <- newForeignPtr_ (castPtr bwImgPtr)
let bwImg = imageFromUnsafePtr width height bwImgForeignPtr
savePngImage outPath (ImageRGBA8 bwImg)
--
BlackWhiteSmoothExport -> do
bwImgPtr <- SCV.otsu_threshold_rgba width height True resutlImg
bwImgForeignPtr <- newForeignPtr_ (castPtr bwImgPtr)
let bwImg = imageFromUnsafePtr width height bwImgForeignPtr
savePngImage outPath (ImageRGBA8 bwImg)
Expand Down
2 changes: 2 additions & 0 deletions source/SimpleCV.chs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Protolude (
identity,
Int,
IO,
Bool,
Ptr,
Show,
return,
Expand Down Expand Up @@ -110,6 +111,7 @@ instance Storable Matrix3x3 where
{#fun otsu_threshold_rgba
{ `Int' -- ^ width
, `Int' -- ^ height
, `Bool' -- ^ whether to use double thresholding
, identity `Ptr CUChar' -- ^ Original image data
} -> `Ptr CUChar' castPtr -- ^ Thresholded image data
#}
Expand Down
15 changes: 11 additions & 4 deletions source/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ data ExportMode
= UnmodifiedExport
| GrayscaleExport
| BlackWhiteExport
| BlackWhiteSmoothExport


data UiComponent
Expand Down Expand Up @@ -196,7 +197,7 @@ data AppState = AppState
appInitialWidth, appInitialHeight, sidebarInitialWidth :: Int
appInitialWidth = 1280
appInitialHeight = 960
sidebarInitialWidth = 150
sidebarInitialWidth = 180


initialState :: AppState
Expand All @@ -218,19 +219,25 @@ initialState =
, uiComponents =
[ Button
{ text = "Save"
, width = 110
, width = 160
, height = 30
, bgColor = 0
}
, Button
{ text = "Save Gray"
, width = 110
, width = 160
, height = 30
, bgColor = 0
}
, Button
{ text = "Save BW"
, width = 110
, width = 160
, height = 30
, bgColor = 0
}
, Button
{ text = "Save BW Smooth"
, width = 160
, height = 30
, bgColor = 0
}
Expand Down
12 changes: 8 additions & 4 deletions source/Utils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,23 @@ getWordSprite spriteText =
Bitmap bitmapData -> case spriteText of
"Save" ->
BitmapSection
Rectangle{rectPos = (0, 40), rectSize = (90, 20)}
Rectangle{rectPos = (-5, 40), rectSize = (150, 20)}
bitmapData
"Save BW" ->
BitmapSection
Rectangle{rectPos = (0, 60), rectSize = (90, 20)}
Rectangle{rectPos = (-5, 60), rectSize = (150, 20)}
bitmapData
"Save Gray" ->
BitmapSection
Rectangle{rectPos = (0, 80), rectSize = (90, 20)}
Rectangle{rectPos = (-5, 80), rectSize = (150, 20)}
bitmapData
"Select Files" ->
BitmapSection
Rectangle{rectPos = (0, 140), rectSize = (100, 20)}
Rectangle{rectPos = (-5, 140), rectSize = (150, 20)}
bitmapData
"Save BW Smooth" ->
BitmapSection
Rectangle{rectPos = (-5, 160), rectSize = (150, 20)}
bitmapData
_ -> mempty
_ -> mempty
Expand Down

0 comments on commit 20e7734

Please sign in to comment.