diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..7ec03ca --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,49 @@ +name: Build + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Restore opam cache + uses: actions/cache@v3 + with: + path: ~/.opam + key: ${{ matrix.os }}-opam-${{ hashFiles('**/*.opam') }} + restore-keys: | + ${{ matrix.os }}-opam- + + - name: Install OCaml and OPAM + uses: ocaml/setup-ocaml@v3 + with: + ocaml-compiler: 5 + + - name: Install dune + run: opam install dune + + - name: Install dependencies + run: | + eval $(opam env) + opam install -y camlimages + + - name: Build with make + run: | + eval $(opam env) + make + + - name: Test build + run: | + eval $(opam env) + ./imageCompressor --help diff --git a/README.md b/README.md index 7e098ae..f9a9359 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ For **image** compression USAGE: ./imageCompressor [OPTIONS] -k K [] OPTIONS: - -h display this message + -h | --help display this message ARGS: K number of colors in the final image @@ -69,7 +69,7 @@ Here are some [input files examples](/examples/text-inputs/) USAGE: ./imageCompressor [OPTIONS] -n N -l L -f F OPTIONS: - -h display this message + -h | --help display this message ARGS: N number of colors in the final image diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..a58d283 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,39 @@ +# Image clusterization - Example + +Here's a pic of one of the best athletes of our time, [Cristiano Ronaldo](https://en.wikipedia.org/wiki/Cristiano_Ronaldo). (I've always been a [FCB](https://img.rts.ch/medias/2009/image/nium1i-27968659.image?w=1200&h=630) supporter though ^^). + +Here's the original picture: + +
+ +
+ +Here are the clustered images, and their respective number of colors : + +
+ +**K = 2** +
+ + +**K = 3** +
+ + +**K = 4** +
+ + +**K = 5** +
+ + +**K = 10** +
+ + +**K = 12** +
+ + +
\ No newline at end of file diff --git a/examples/inputs/cristiano-ronaldo.jpg b/examples/inputs/cristiano-ronaldo.jpg new file mode 100644 index 0000000..79523ed Binary files /dev/null and b/examples/inputs/cristiano-ronaldo.jpg differ diff --git a/examples/outputs/ronaldo/ronaldo_k10.jpg b/examples/outputs/ronaldo/ronaldo_k10.jpg new file mode 100644 index 0000000..df58afe Binary files /dev/null and b/examples/outputs/ronaldo/ronaldo_k10.jpg differ diff --git a/examples/outputs/ronaldo/ronaldo_k12.jpg b/examples/outputs/ronaldo/ronaldo_k12.jpg new file mode 100644 index 0000000..6c09d3a Binary files /dev/null and b/examples/outputs/ronaldo/ronaldo_k12.jpg differ diff --git a/examples/outputs/ronaldo/ronaldo_k2.jpg b/examples/outputs/ronaldo/ronaldo_k2.jpg new file mode 100644 index 0000000..ea48ae5 Binary files /dev/null and b/examples/outputs/ronaldo/ronaldo_k2.jpg differ diff --git a/examples/outputs/ronaldo/ronaldo_k3.jpg b/examples/outputs/ronaldo/ronaldo_k3.jpg new file mode 100644 index 0000000..b06cec9 Binary files /dev/null and b/examples/outputs/ronaldo/ronaldo_k3.jpg differ diff --git a/examples/outputs/ronaldo/ronaldo_k4.jpg b/examples/outputs/ronaldo/ronaldo_k4.jpg new file mode 100644 index 0000000..6726237 Binary files /dev/null and b/examples/outputs/ronaldo/ronaldo_k4.jpg differ diff --git a/examples/outputs/ronaldo/ronaldo_k5.jpg b/examples/outputs/ronaldo/ronaldo_k5.jpg new file mode 100644 index 0000000..5d85d40 Binary files /dev/null and b/examples/outputs/ronaldo/ronaldo_k5.jpg differ diff --git a/src/centroid.ml b/src/centroid.ml index 1f1fc39..077a9d7 100644 --- a/src/centroid.ml +++ b/src/centroid.ml @@ -4,8 +4,6 @@ File: centroid.ml *) -open Math - (* Returns [(d, c)] where 'd' is color distance between centroid 'c' and input color *) let with_distances centroids color = centroids diff --git a/src/dune b/src/dune index 4af32f2..dc1050f 100644 --- a/src/dune +++ b/src/dune @@ -2,17 +2,5 @@ (executable (public_name imageCompressor) (name main) - (libraries camlimages.core camlimages.gif camlimages.jpeg camlimages.png) - (modules - main - utils - file_io - types - math - parse_file - k_means - centroid - colors - image - ) + (libraries camlimages.core camlimages.jpeg camlimages.png) ) \ No newline at end of file diff --git a/src/image/image.ml b/src/image/image.ml index 3854b35..3ec6874 100644 --- a/src/image/image.ml +++ b/src/image/image.ml @@ -4,13 +4,16 @@ File: images.ml *) -open Images +(* Aliases to distinguish CamlImages modules *) +module CI_Images = Images +module CI_Rgb24 = Rgb24 + open Types open K_means open File_io let get_pixel img x y = - let rgb = Rgb24.get img x y in + let rgb = CI_Rgb24.get img x y in (x, y), (rgb.r, rgb.g, rgb.b) let get_all_pixels img w h = @@ -22,9 +25,9 @@ let get_all_pixels img w h = let read_image filepath = try - let img = Images.load filepath [] in + let img = CI_Images.load filepath [] in match img with - | Rgb24 img -> (* Only handles Rgb24 format *) + | CI_Images.Rgb24 img -> (* Only handles Rgb24 format *) let width = img.width in let height = img.height in let pixels = get_all_pixels img width height @@ -37,19 +40,21 @@ let color_to_target (r, g, b) = { Color.r; g; b } let reconstruct_image (pixels: pixel list) w h = (* Create blank image *) - let img = Rgb24.create w h in + let img = CI_Rgb24.create w h in (* Fill pixels *) List.iter (fun ((x,y), color) -> - Rgb24.set img x y (color_to_target color) + CI_Rgb24.set img x y (color_to_target color) ) pixels; img -let save_image (img: Rgb24.t) input_filepath (out_filepath: string option) = - match out_filepath with - | Some name -> - let out_filepath = handle_out_name input_filepath name in - Images.save out_filepath None [] (Images.Rgb24 img) - | None -> Images.save input_filepath None [] (Images.Rgb24 img) +let save_image (img: CI_Rgb24.t) input_filepath (out_filepath: string option) = + let img = CI_Images.Rgb24 img in + let target_path = + match out_filepath with + | Some name -> handle_out_name input_filepath name + | None -> input_filepath + in + CI_Images.save target_path None [] img let compress_image k pixels w h = let k = int_of_string k in diff --git a/src/main.ml b/src/main.ml index 8d70c99..695d49b 100644 --- a/src/main.ml +++ b/src/main.ml @@ -42,7 +42,7 @@ let process_opt_arg rest = let main() = let args = Sys.argv in match Array.to_list args with - | [ _; "-h" ] -> usage 0; + | [ _; h ] when is_help_flag h -> usage 0; | _ :: "-k" :: k :: img_filepath :: rest -> let (img_pixels, (width, height)) = get_image_content img_filepath in let image = compress_image k img_pixels width height in diff --git a/src/math.ml b/src/math.ml index 9758c11..a7a04f5 100644 --- a/src/math.ml +++ b/src/math.ml @@ -4,20 +4,16 @@ File: math.ml *) -module Math = struct - (* - Calculates the euclidean distance between two 3d integer tuples. - *) - let euclidean_distance a b: float = - let (x1, y1, z1) = a in - let (x2, y2, z2) = b in - let dx = float_of_int (x2 - x1) in - let dy = float_of_int (y2 - y1) in - let dz = float_of_int(z2 - z1) in - sqrt( (dx *. dx) +. (dy *. dy) +. (dz *. dz) ) +(* Calculates the euclidean distance between two 3d integer tuples. *) +let euclidean_distance a b: float = + let (x1, y1, z1) = a in + let (x2, y2, z2) = b in + let dx = float_of_int (x2 - x1) in + let dy = float_of_int (y2 - y1) in + let dz = float_of_int(z2 - z1) in + sqrt( (dx *. dx) +. (dy *. dy) +. (dz *. dz) ) - (* Returns the euclidean distance between two RGB colors as (r,g,b) tuples. *) - let color_distance (r1, g1, b1) (r2, g2, b2) = - euclidean_distance (r1, g1, b1) (r2, g2, b2) -end \ No newline at end of file +(* Returns the euclidean distance between two RGB colors as (r,g,b) tuples. *) +let color_distance (r1, g1, b1) (r2, g2, b2) = + euclidean_distance (r1, g1, b1) (r2, g2, b2) diff --git a/src/utils.ml b/src/utils.ml index 198bb07..cc13fcc 100644 --- a/src/utils.ml +++ b/src/utils.ml @@ -4,11 +4,15 @@ File: utils.ml *) +let help_flag = ["-h";"--help"] + +let is_help_flag arg = List.mem arg help_flag + let image_comp_usage = "IMAGE COMPRESSION MODE\n\n" ^ "\t./imageCompressor [OPTIONS] -k K [] \n\n" ^ "\tOPTIONS:\n" - ^ "\t -h display this message\n\n" + ^ "\t -h | --help display this message\n\n" ^ "\tARGS:\n" ^ "\t K number of colors in the final image\n\n" ^ "\t optional path/name for the compressed image @@ -23,7 +27,7 @@ let print_clustering_usage = ^ "\t\n" ^ "\t./imageCompressor [OPTIONS] -n N -l L -f F \n\n" ^ "\tOPTIONS:\n" - ^ "\t -h display this message\n\n" + ^ "\t -h | --help display this message\n\n" ^ "\tARGS:\n" ^ "\t N number of colors in the final image\n" ^ "\t L convergence limit\n"