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
- Select an file, a group of files or a directory
- Select an output directory where the resized images will be output.
- Select a prefix which will be appended to the input’s original filenames (to avoid confusion and hamper the risk of overwriting).
- 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.
- 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)
