Pull to refresh

Сжатие картинок под нужный размер в Windows

Reading time 7 min
Views 1.8K
Хочу поделиться с ХабраЛюдьми одной АйТишной историей, что произошла со мной в Новогодние праздники. Одним из моих новогодних подарков стала цифровая фоторамка от DICOM(вполне приличная, кстати, вещь). Содержит она в себе 128 МБ встроенной памяти и удобный интерфейс для заливки фоток — ака флэшка. Но вот ни задача — в домашнем архиве на компе фотки хранятся полноразмерные, а вот на флешке они ужимаются до малых размеров и показываются умиленному пользователю. Надо как то бороться с расточительством — сжимать каждую фотку ручками дело хлопотное!

Тут, хочу отметить что сам я виндой не особо пользуюсь — Линукс, как то привычней… И вот там бы взять imagemagick, да bash, да написать бы скрипт… Кстати, только сегодня был пост на хабре на эту тему. Да только вот ситуация в винде повергла меня в раздумья.

Конечно, наверняка есть специфический софт, но душа программиста требует системного подхода) Вобщем, мне стало интересно, как такие задачи можно решить в Винде. Юзать виндовый shell сразу отказался(не спрашивайте почему). Решил написать прожку на Java. Собственно это и было сделано — на вход подается папка, в ней рекурсивно ищутся все файлы с нужным расширением, для них высчитывается нужно ли их ужимать и собственно происходит масштабирование.
Привожу код(в Java я новичок, так что не ругайте, но конструктивная критика очень интересна):

import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import javax.swing.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.LinkedList;

public class ImageConverter {

  static int MAX_WIDTH = 480;
  static int MAX_HEIGHT = 360;

  public static void main(String[] args) {
    String indir = "";
    if(args.length > 0){
      indir = args[0];
    }else{
      System.out.println("Usage: imageconv path [maxwidth] [maxheight]");
      return;
    }
    if(args.length > 2){
      MAX_WIDTH = Integer.parseInt(args[1]);
      MAX_HEIGHT = Integer.parseInt(args[2]);
    }
    System.out.println("Input directory: " + indir);
    LinkedList<File> files = getTree(new File(indir));
    if(files.size() == 0){
      JOptionPane.showMessageDialog(null, "Nothing to process",
          "Image Convert Error",JOptionPane.ERROR_MESSAGE);
      return;
    }
    int startSize = 0, endSize = 0;
    int res = JOptionPane.showConfirmDialog(null, "Will process " + files.size() + " files on drive " + indir,
        "Image Convert",JOptionPane.OK_CANCEL_OPTION);
    if(res == JOptionPane.CANCEL_OPTION) return;
    for(File original: files){
      startSize += original.length();
      System.out.print("Will process " + original.getName() + "...");
      try{
        if(resize(original, original)){
          System.out.println("Done!");
        }else{
          System.out.println("No conversion.");
        }
      }catch(Exception e){
        System.out.println(" Error: " + e.getMessage());
      }
    }
    for(File result: files){
      endSize += result.length();
    }
    System.out.println("Result is: start size " + startSize + ", end size is "
        + endSize);
    System.out.println("Comperession is " + (100.0*(startSize - endSize) / startSize) + "%.");
    
    return;
  }
  public static LinkedList<File> getTree(File dir){
    LinkedList<File> result = new LinkedList<File>();
    if(dir.isFile()){
      String fname = dir.getName();
      String extension = fname;
      if(fname.length()>3)
        extension = fname.substring(fname.length() - 3).toLowerCase();
      if("jpg".equals(extension)){
        result.add(dir);
      }
      return result;
    }
    File[] children = dir.listFiles();
    if(children == null) return result;
    for(File s: children){
      result.addAll(getTree(s));
    }
    return result;
  }

  public static boolean resize(File originalFile, File resizedFile) throws IOException {
  
      ImageIcon ii = new ImageIcon(originalFile.getCanonicalPath());
      Image i = ii.getImage();
      Image resizedImage = null;
  
      int newWidth = i.getWidth(null);
      int newHeight = i.getHeight(null);
  
      double aspectW = 1.0 * newWidth / ImageConverter.MAX_WIDTH;
      double aspectH = 1.0 * newHeight / ImageConverter.MAX_HEIGHT;      
      System.out.println("AspectW: " + aspectW + "; AspectH: " + aspectH);
      if(aspectW > 1.0 || aspectH > 1.0){
        if(aspectW >= aspectH){ // resize by width
          newHeight = new Double(newHeight / aspectW).intValue();
          newWidth = ImageConverter.MAX_WIDTH;
        }else{//resize by height
          newWidth = new Double(newWidth / aspectH).intValue();
          newHeight = ImageConverter.MAX_HEIGHT;
        }
        
        resizedImage = i.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
        Image temp = new ImageIcon(resizedImage).getImage();
    
        BufferedImage bufferedImage = new BufferedImage(temp.getWidth(null), temp.getHeight(null),
                                BufferedImage.TYPE_INT_RGB);
        Graphics g = bufferedImage.createGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, temp.getWidth(null), temp.getHeight(null));
        g.drawImage(temp, 0, 0, null);
        g.dispose();
    
        FileOutputStream out = new FileOutputStream(resizedFile);
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
        JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bufferedImage);    
        param.setQuality(1.0f, true);
        encoder.setJPEGEncodeParam(param);
        encoder.encode(bufferedImage);
        return true;
      }else{///nothing to do
        return false;
      }
    }
  }

* This source code was highlighted with Source Code Highlighter.


Но как эту прогу запускать? Писать пользоветльский интерфейс для такой задачи было бы опять же расточительно. ПодумавПогуглив решил встроить эту прогу в контекстное меню для устройств. Однако хочется еще видеть отладочную печать(не столько для отладки, а сколько для понимания что процесс движется). Решил сделать так — cmd-файл, как обертка для проги(все таки заюзал виндовый shell). Итак, делаем из класса jar файл, кладем его в C:/Program Files/imgconv/imgconv.jar, делаем cmd файл ConvertImages.cmd:
java -jar "C:\Program Files\imgconv\imgconv.jar" %1
pause

(Естественно исполняемый файл java должен быть доступен через переменную PATH)
Теперь осталось добавить элемент меню. Делаем так:
  • Открываем реестр в HKEY_CLASSES_ROOT\Drive\shell\
  • Создаем раздел Image Converter(Это и будет именем элемента меню)
  • В нем создаем раздел command
  • Устанавливаем в нем значение по умолчанию C:\Program Files\imgconv\ConvertImages.cmd %1
  • Проверяем

Получилось очень даже удобно — запускаем, проверяем то ли мы хотим ресайзить, жмем ок и медитируем на процесс.
P.S.Данный опус не является советом, руководством или чем-либо еще в том же духе. Просто рассказ о том как имея свободное время я пытался придумать нестандартное решение стандартной проблемы)

P.P.S. Всех с Новым Годом!
Tags:
Hubs:
+12
Comments 22
Comments Comments 22

Articles