Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ For **image** compression
USAGE: ./imageCompressor [OPTIONS] -k K <input_image_filepath> [<output_filepath>]

OPTIONS:
-h display this message
-h | --help display this message

ARGS:
K number of colors in the final image
Expand All @@ -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
Expand Down
39 changes: 39 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -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:

<div align="center">
<img src="inputs/cristiano-ronaldo.jpg" width="400"/>
</div>

Here are the clustered images, and their respective number of colors :

<div align="center">

**K = 2**
<br>
<img src="outputs/ronaldo/ronaldo_k2.jpg" width="400"/>

**K = 3**
<br>
<img src="outputs/ronaldo/ronaldo_k3.jpg" width="400"/>

**K = 4**
<br>
<img src="outputs/ronaldo/ronaldo_k4.jpg" width="400"/>

**K = 5**
<br>
<img src="outputs/ronaldo/ronaldo_k5.jpg" width="400"/>

**K = 10**
<br>
<img src="outputs/ronaldo/ronaldo_k10.jpg" width="400"/>

**K = 12**
<br>
<img src="outputs/ronaldo/ronaldo_k12.jpg" width="400"/>

</div>
Binary file added examples/inputs/cristiano-ronaldo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/outputs/ronaldo/ronaldo_k10.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/outputs/ronaldo/ronaldo_k12.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/outputs/ronaldo/ronaldo_k2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/outputs/ronaldo/ronaldo_k3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/outputs/ronaldo/ronaldo_k4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/outputs/ronaldo/ronaldo_k5.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 0 additions & 2 deletions src/centroid.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 1 addition & 13 deletions src/dune
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
29 changes: 17 additions & 12 deletions src/image/image.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 11 additions & 15 deletions src/math.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
(* 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)
8 changes: 6 additions & 2 deletions src/utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <input_image_filepath> [<output_filepath>] \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 <output_filepath> optional path/name for the compressed image
Expand All @@ -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"
Expand Down