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)