The batch image resizer’s goal is to allow the resizing of many images at once while preserving their aspect ratio (i.e. width / height proportions).

Use

  1. Select an file, a group of files or a directory
  2. Select an output directory where the resized images will be output.
  3. Select a prefix which will be appended to the input’s original filenames (to avoid confusion and hamper the risk of overwriting).
  4. Choose the lngth of the longest side of the output picture. That is, if the image’s orientation was landscape, then the longest side will refer to the ouput image’s width.
  5. Run the program!

Code and compiling

To compile the code with the F# compiler, run :

fsc --target winexe --standalone -r "Fsharp.Powerpack.dll" -o "applicationName.exe" "imagebatchresizer.fs"

open System
open System.Drawing
open System.Drawing.Drawing2D
open System.IO
open System.Windows.Forms

//---------------------------------------------------------------
// Helpers
//---------------------------------------------------------------

let acceptedFormats =
  [ ".BMP"; ".GIF"; ".EXIG";".JPG"; ".PNG"; ".TIFF"]

let isAcceptable (s:string) =
  acceptedFormats  |> List.exists (fun format -> s.ToUpper().EndsWith format)

let filesStore : ref<Set<string>> = ref Set.empty

let rec DirectoryFiles directory useRecursion =
  [ for file in Directory.GetFiles directory do
      if isAcceptable file then
        yield file
    if useRecursion then
      for subDirectory in Directory.GetDirectories directory do
        yield! DirectoryFiles subDirectory useRecursion
  ] |> Set.of_list

//---------------------------------------------------------------
// File Processing
//---------------------------------------------------------------

let AsyncProcessFile file outputDirectory prefix longSide =
  async
    { if File.Exists(file) then
        try
          let img = new Bitmap(file)

          let ratio = float (if img.Width > img.Height then img.Width else img.Height) / longSide
          let w = int <| Math.Round(float img.Width / ratio)
          let h = int <| Math.Round(float img.Height / ratio)

          let dst = new Bitmap(w, h)

          use g = Graphics.FromImage(dst)
          g.SmoothingMode <- SmoothingMode.HighQuality
          g.InterpolationMode <- InterpolationMode.HighQualityBicubic

          let imgRect = new RectangleF(0.0f, 0.0f, single img.Width, single img.Height)
          let dstRect = new RectangleF(0.0f, 0.0f, single w, single h)
          g.DrawImage(img, dstRect, imgRect, GraphicsUnit.Pixel)

          let dir = defaultArg outputDirectory (Filename.dirname file)
          let newFileName = prefix + Filename.basename file
          let path = Filename.concat dir newFileName
          dst.Save(path, img.RawFormat)
        with
          e -> ()
    }

let ProcessFiles files outputDir prefix longSide =
  for file in files do
    Async.Start <| AsyncProcessFile file outputDir prefix longSide

//---------------------------------------------------------------
// Main Form
//---------------------------------------------------------------

let mainForm =
  new Form(
    AutoSize = true,
    AutoSizeMode = AutoSizeMode.GrowAndShrink,
    Text = "Resize Pictures"
  )

let mainPanel =
  new FlowLayoutPanel(
    AutoSize = true,
    AutoSizeMode = AutoSizeMode.GrowAndShrink,
    Dock = DockStyle.Fill,
    FlowDirection = FlowDirection.TopDown
  )

let run =
  new Button(
    AutoSize = true,
    Enabled = false,
    Text = "Run"
  )

let label() = new Label( AutoSize = true )
let button txt = new Button( AutoSize = true, Text = txt)

//---------------------------------------------------------------
// File selection on a per-file basis
//---------------------------------------------------------------

let files = label()

let browseFiles = button "Select files..."

let formats = acceptedFormats |> List.map (fun s -> "*" + s) |> String.concat ";"

let filesBrowser =
  new OpenFileDialog(
    Multiselect = true,
    Title = "Select files to shrink",
    Filter = "Image Files(" +  formats + ")|" + formats
  )

let clearFiles =
  let btn = button "Clear"
  btn.Enabled <- false
  btn

let filesPanel = new FlowLayoutPanel( AutoSize = true )

filesPanel.Controls.Add(browseFiles)
filesPanel.Controls.Add(clearFiles)

browseFiles.Click.Add(fun _ ->
  if filesBrowser.ShowDialog() = DialogResult.OK then
    if not run.Enabled then run.Enabled <- true
    for file in filesBrowser.FileNames do
      if not <| Set.contains file !filesStore then
        filesStore := Set.add file !filesStore
        files.Text <- files.Text + "\n" + file
    clearFiles.Enabled <- true
)

clearFiles.Click.Add(fun _ ->
  files.Text <- ""
  clearFiles.Enabled <- false
  filesStore := Set.empty
)

//---------------------------------------------------------------
// File selection on a per-directory basis
//---------------------------------------------------------------

let directory = label()

let browseDirectory = button "Select directory..."

let directoryBrowser = new FolderBrowserDialog()

let clearDirectory =
  let btn = button "Clear"
  btn.Enabled <- false
  btn

let includeSubdirectories = new CheckBox(Checked = false, AutoSize = true )
let includeSubdirectoriesLabel =
  let lbl = label()
  lbl.Text <- "Include sub-directories"
  lbl

let directoryPanel = new FlowLayoutPanel( AutoSize = true )

directoryPanel.Controls.Add(browseDirectory)
directoryPanel.Controls.Add(clearDirectory)
directoryPanel.Controls.Add(includeSubdirectories)
directoryPanel.Controls.Add(includeSubdirectoriesLabel)

browseDirectory.Click.Add(fun _ ->
  if directoryBrowser.ShowDialog() = DialogResult.OK then
    if not run.Enabled then run.Enabled <- true
    directory.Text <- directoryBrowser.SelectedPath
    clearDirectory.Enabled <- true
)

clearDirectory.Click.Add(fun _ ->
  directory.Text <- ""
  clearDirectory.Enabled <- false
)

//---------------------------------------------------------------
// Output directory selection
//---------------------------------------------------------------

let outputDirectory =
  let lbl = label()
  lbl.Width <- mainForm.Width / 2
  lbl

let outputBrowser =
  new FolderBrowserDialog()

let browseOutput =  button "Select output directory..."

let clearOutput =
  let btn = button "Clear"
  btn.Enabled <- false
  btn

let outputPanel = new FlowLayoutPanel( AutoSize = true )

outputPanel.Controls.Add(browseOutput)
outputPanel.Controls.Add(clearOutput)

browseOutput.Click.Add(fun _ ->
  if outputBrowser.ShowDialog() = DialogResult.OK then
    outputDirectory.Text <- outputBrowser.SelectedPath
    clearOutput.Enabled <- true
)

clearOutput.Click.Add(fun _ ->
  outputDirectory.Text <- ""
  clearOutput.Enabled <- false
)

//---------------------------------------------------------------
// New images name prefix selection
//---------------------------------------------------------------

let prefix =
  new TextBox(
    Text = "resized_",
    Width = 75
  )

let prefixLabel =
  let lbl = label()
  lbl.Text <- "Prefix for output files"
  lbl

let prefixPanel =
  new FlowLayoutPanel(
    AutoSize = true
  )

prefixPanel.Controls.Add(prefix)
prefixPanel.Controls.Add(prefixLabel)

//---------------------------------------------------------------
// New images size of the longest side
// (width or height, depending on the initial file size) selection
//---------------------------------------------------------------

let updown =
  let updown = new NumericUpDown()
  updown.ThousandsSeparator <- true
  updown.Minimum <- 1M
  updown.Maximum <- System.Decimal.MaxValue
  updown.Value <- 800M
  updown.DecimalPlaces <- 0
  updown.Increment <- 50M
  updown.Width <- 75
  updown

let updownLabel =
  let lbl = label()
  lbl.Text <- "Longest side size in pixels"
  lbl

let updownPanel =
  new FlowLayoutPanel(
    AutoSize = true
  )

updownPanel.Controls.Add(updown)
updownPanel.Controls.Add(updownLabel)

//---------------------------------------------------------------
// Global form set-up
//---------------------------------------------------------------

mainPanel.Controls.Add(files)
mainPanel.Controls.Add(filesPanel)
mainPanel.Controls.Add(directory)
mainPanel.Controls.Add(directoryPanel)
mainPanel.Controls.Add(outputDirectory)
mainPanel.Controls.Add(outputPanel)
mainPanel.Controls.Add(prefixPanel)
mainPanel.Controls.Add(updownPanel)
mainPanel.Controls.Add(run)
mainForm.Controls.Add(mainPanel)

run.Click.Add(fun _ ->
  run.Enabled <- false
  let dir = if String.length outputDirectory.Text > 0 then Some outputDirectory.Text else None
  let directoryFiles =
    if directory.Text.Length > 0 then
      DirectoryFiles directory.Text includeSubdirectories.Checked
    else
      Set.empty
  let files = !filesStore + directoryFiles
  ProcessFiles files dir prefix.Text (float updown.Value)
  run.Enabled <- true
)

[<STAThread>]
Application.EnableVisualStyles()
Application.Run(mainForm)

Comments are closed.