Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportAction.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportAction.java	(revision 32033)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportAction.java	(revision 32034)
@@ -12,14 +12,12 @@
 
 import javax.swing.JFileChooser;
-import javax.swing.filechooser.FileNameExtensionFilter;
 
-import org.apache.commons.imaging.ImageReadException;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.JosmAction;
 import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
-import org.openstreetmap.josm.plugins.mapillary.MapillaryLayer;
 import org.openstreetmap.josm.plugins.mapillary.MapillaryPlugin;
 import org.openstreetmap.josm.plugins.mapillary.history.MapillaryRecord;
 import org.openstreetmap.josm.plugins.mapillary.history.commands.CommandImport;
+import org.openstreetmap.josm.plugins.mapillary.utils.ImageUtil;
 import org.openstreetmap.josm.plugins.mapillary.utils.MapillaryUtils;
 import org.openstreetmap.josm.tools.Shortcut;
@@ -60,5 +58,5 @@
     chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
     chooser.setAcceptAllFileFilterUsed(false);
-    chooser.addChoosableFileFilter(new FileNameExtensionFilter("images", "jpg", "jpeg", "png"));
+    chooser.addChoosableFileFilter(ImageUtil.IMAGE_FILE_FILTER);
     chooser.setMultiSelectionEnabled(true);
     if (chooser.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) {
@@ -66,32 +64,11 @@
       for (File file : chooser.getSelectedFiles()) {
         Main.pref.put("mapillary.start-directory", file.getParent());
-        MapillaryLayer.getInstance();
-        if (file.isDirectory()) {
-          if (file.listFiles() == null)
-            continue;
-          for (File innerFile : file.listFiles()) {
-            String extension = MapillaryUtils.getExtension(innerFile);
-            try {
-              if ("jpg".equals(extension) || "jpeg".equals(extension))
-                images.add(MapillaryUtils.readJPG(innerFile));
-              else if ("png".equals(extension))
-                images.add(MapillaryUtils.readPNG(innerFile));
-            } catch (ImageReadException | IOException | NullPointerException e1) {
-              Main.error(e1);
-            }
-          }
-        } else {
-          String extension = MapillaryUtils.getExtension(file);
-          if ("jpg".equals(extension) || "jpeg".equals(extension)) {
-            try {
-              images.add(MapillaryUtils.readJPG(file));
-            } catch (ImageReadException ex) {
-              Main.error(ex);
-            } catch (IOException ex) {
-              Main.error(ex);
-            }
-          } else if (".png".equals(file.getPath().substring(file.getPath().length() - 4))) {
-            images.add(MapillaryUtils.readPNG(file));
-          }
+        try {
+          images.addAll(ImageUtil.readImagesFrom(
+              file,
+              Main.map.mapView.getProjection().eastNorth2latlon(Main.map.mapView.getCenter())
+          ));
+        } catch (IOException e) {
+          Main.error("Could not read image(s) from "+file.getAbsolutePath());
         }
       }
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportIntoSequenceAction.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportIntoSequenceAction.java	(revision 32033)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/actions/MapillaryImportIntoSequenceAction.java	(revision 32034)
@@ -15,7 +15,5 @@
 
 import javax.swing.JFileChooser;
-import javax.swing.filechooser.FileNameExtensionFilter;
 
-import org.apache.commons.imaging.ImageReadException;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.JosmAction;
@@ -25,8 +23,7 @@
 import org.openstreetmap.josm.plugins.mapillary.history.MapillaryRecord;
 import org.openstreetmap.josm.plugins.mapillary.history.commands.CommandImport;
+import org.openstreetmap.josm.plugins.mapillary.utils.ImageUtil;
 import org.openstreetmap.josm.plugins.mapillary.utils.MapillaryUtils;
 import org.openstreetmap.josm.tools.Shortcut;
-
-import org.openstreetmap.josm.plugins.mapillary.MapillaryLayer;
 
 /**
@@ -65,36 +62,17 @@
     chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
     chooser.setAcceptAllFileFilterUsed(false);
-    chooser.addChoosableFileFilter(new FileNameExtensionFilter("images", "jpg", "jpeg"));
+    chooser.addChoosableFileFilter(ImageUtil.IMAGE_FILE_FILTER);
     chooser.setMultiSelectionEnabled(true);
 
     if (chooser.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) {
       for (File file : chooser.getSelectedFiles()) {
-        if (file == null)
-          break;
         Main.pref.put("mapillary.start-directory", file.getParent());
-        MapillaryLayer.getInstance();
-        if (file.isDirectory()) {
-          for (File file2 : file.listFiles()) {
-            String extension = MapillaryUtils.getExtension(file2);
-            try {
-              if ("jpg".equals(extension) || "jpeg".equals(extension))
-                MapillaryUtils.readJPG(file2, true);
-            } catch (ImageReadException | NullPointerException | IOException e) {
-              Main.error(e);
-            }
-          }
-        } else {
-          String extension = MapillaryUtils.getExtension(file);
-          if ("jpg".equals(extension) || "jpeg".equals(extension)) {
-            try {
-              this.images.add(MapillaryUtils.readJPG(file, true));
-            } catch (ImageReadException ex) {
-              Main.error(ex);
-            } catch (IOException ex) {
-              Main.error(ex);
-            } catch (IllegalArgumentException ex) {
-              // Ignored image.
-            }
-          }
+        try {
+          images.addAll(ImageUtil.readImagesFrom(
+              file,
+              Main.map.mapView.getProjection().eastNorth2latlon(Main.map.mapView.getCenter())
+          ));
+        } catch (IOException e) {
+          Main.error("Could not read image(s) from "+file.getAbsolutePath());
         }
       }
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/ImageUtil.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/ImageUtil.java	(revision 32034)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/ImageUtil.java	(revision 32034)
@@ -0,0 +1,165 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins.mapillary.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.filechooser.FileFilter;
+
+import org.apache.commons.imaging.ImageReadException;
+import org.apache.commons.imaging.Imaging;
+import org.apache.commons.imaging.common.ImageMetadata;
+import org.apache.commons.imaging.common.RationalNumber;
+import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
+import org.apache.commons.imaging.formats.tiff.TiffField;
+import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
+import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
+import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.plugins.mapillary.MapillaryImportedImage;
+import org.openstreetmap.josm.tools.I18n;
+
+public final class ImageUtil {
+  public static final FileFilter IMAGE_FILE_FILTER = new ImageFileFilter();
+
+  private ImageUtil() {
+    // Private constructor to avoid instantiation
+  }
+
+  /**
+   * Recursive method to retrieve all images inside a directory or in a single file.
+   * @param f the file or directory from which all contained images should be read
+   * @param defaultLL the coordinates that the images should get, if no coordinates are found in metadata
+   * @return all images that were found within the given file or directory
+   * @throws IOException if the content of the file could not be read
+   */
+  public static List<MapillaryImportedImage> readImagesFrom(final File f, final LatLon defaultLL) throws IOException {
+    List<MapillaryImportedImage> images = new ArrayList<>();
+    if (!f.exists() || !f.canRead()) {
+      throw new IOException(f.getAbsolutePath() + " not found or not readable!");
+    } else if (f.isDirectory()) {
+      for (File child : f.listFiles()) {
+        try {
+          images.addAll(readImagesFrom(child, defaultLL));
+        } catch (IOException e) {
+          // Don't throw an exception here to allow other files that might be readable to be read.
+          // Might not be the best solution, but the easiest workaround I could imagine.
+          Main.error(f.getAbsolutePath() + " not found or not readable!");
+        }
+      }
+    } else if (IMAGE_FILE_FILTER.accept(f)) {
+      try (FileInputStream fis = new FileInputStream(f)) {
+        images.add(readImageFrom(fis, f, defaultLL));
+      }
+    }
+    return images;
+  }
+
+  /**
+   * @param is the input stream to read the metadata from
+   * @param f the file that will be set as a field to the returned {@link MapillaryImportedImage}
+   * @param defaultLL the coordinates that the image should get, if no coordinates are found in metadata
+   * @return the {@link MapillaryImportedImage} with the read metadata and the given file set
+   * @throws IOException if an IOException occurs while reading from the input stream
+   */
+  private static MapillaryImportedImage readImageFrom(
+      final InputStream is, final File f, final LatLon defaultLL
+  ) throws IOException {
+    Object latRef = null;
+    Object lonRef = null;
+    Object lat = null;
+    Object lon = null;
+    Object gpsDir = null;
+    Object dateTime = null;
+    final ImageMetadata meta;
+    try {
+      meta = Imaging.getMetadata(is, null);
+      if (meta instanceof JpegImageMetadata) {
+        final JpegImageMetadata jpegMeta = (JpegImageMetadata) meta;
+        latRef = getTiffFieldValue(jpegMeta, GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
+        lonRef = getTiffFieldValue(jpegMeta, GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
+        lat = getTiffFieldValue(jpegMeta, GpsTagConstants.GPS_TAG_GPS_LATITUDE);
+        lon = getTiffFieldValue(jpegMeta, GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
+        gpsDir = getTiffFieldValue(jpegMeta, GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
+        dateTime = getTiffFieldValue(jpegMeta, ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
+      }
+    } catch (ImageReadException e) {
+      // Can't read metadata from image, use defaults instead
+    }
+
+
+    final LatLon latLon;
+    if (lat instanceof RationalNumber[] && latRef != null && lon instanceof RationalNumber[] && lonRef != null) {
+      latLon = new LatLon(
+          MapillaryUtils.degMinSecToDouble((RationalNumber[]) lat, latRef.toString()),
+          MapillaryUtils.degMinSecToDouble((RationalNumber[]) lon, lonRef.toString())
+      );
+    } else {
+      latLon = defaultLL;
+    }
+    final double ca;
+    if (gpsDir instanceof RationalNumber) {
+      ca = ((RationalNumber) gpsDir).doubleValue();
+    } else {
+      ca = 0;
+    }
+    if (dateTime == null) {
+      return new MapillaryImportedImage(latLon, ca, f);
+    }
+    return new MapillaryImportedImage(latLon, ca, f, dateTime.toString());
+  }
+
+  private static Object getTiffFieldValue(JpegImageMetadata meta, TagInfo tag) {
+    TiffField field = meta.findEXIFValueWithExactMatch(tag);
+    if (field != null) {
+      try {
+        return field.getValue();
+      } catch (ImageReadException e) {
+        // If value couldn't be read, assume it's not set.
+      }
+    }
+    return null;
+  }
+
+  private static class ImageFileFilter extends FileFilter {
+    private static final byte[] JFIF_MAGIC = new byte[]{-1 /*0xFF*/, -40 /*0xD8*/};
+    private static final byte[] PNG_MAGIC = new byte[]{
+        -119 /*0x89*/, 80 /*0x50*/, 78 /*0x4E*/, 71 /*0x47*/, 13 /*0x0D*/, 10 /*0x0A*/, 26 /*0x1A*/, 10 /*0x0A*/
+    };
+    private final byte[] magic = new byte[Math.max(JFIF_MAGIC.length, PNG_MAGIC.length)];
+
+    ImageFileFilter() { }
+
+    @Override
+    public synchronized boolean accept(File f) {
+      if (!f.canRead() || !f.exists()) {
+        return false;
+      }
+      if (f.isDirectory()) {
+        return true;
+      }
+      try (FileInputStream fis = new FileInputStream(f)) {
+        int numBytes = fis.read(magic);
+        return Arrays.equals(JFIF_MAGIC, Arrays.copyOf(magic, Math.min(numBytes, JFIF_MAGIC.length)))
+            || Arrays.equals(PNG_MAGIC, Arrays.copyOf(magic, Math.min(numBytes, PNG_MAGIC.length)));
+      } catch (FileNotFoundException e) {
+        return false;
+      } catch (IOException e) {
+        return false;
+      }
+    }
+
+    @Override
+    public String getDescription() {
+      return I18n.tr("Supported image formats (JPG and PNG)");
+    }
+
+  }
+}
Index: applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryUtils.java
===================================================================
--- applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryUtils.java	(revision 32033)
+++ applications/editors/josm/plugins/mapillary/src/org/openstreetmap/josm/plugins/mapillary/utils/MapillaryUtils.java	(revision 32034)
@@ -5,8 +5,4 @@
 
 import java.awt.Desktop;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.geom.Area;
-import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
@@ -21,19 +17,11 @@
 import javax.swing.SwingUtilities;
 
-import org.apache.commons.imaging.ImageReadException;
-import org.apache.commons.imaging.Imaging;
-import org.apache.commons.imaging.common.ImageMetadata;
 import org.apache.commons.imaging.common.RationalNumber;
-import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
-import org.apache.commons.imaging.formats.tiff.TiffField;
-import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
 import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage;
 import org.openstreetmap.josm.plugins.mapillary.MapillaryData;
-import org.openstreetmap.josm.plugins.mapillary.MapillaryImportedImage;
 import org.openstreetmap.josm.plugins.mapillary.MapillaryLayer;
 import org.openstreetmap.josm.plugins.mapillary.MapillarySequence;
@@ -165,20 +153,4 @@
 
   /**
-   * Returns the extension of a {@link File} object.
-   *
-   * @param file The {@link File} object whose extension is going to be returned.
-   * @return A {@code String} object containing the extension in lowercase.
-   */
-  public static String getExtension(File file) {
-    if (file.isDirectory())
-      throw new IllegalArgumentException("The file is a directory");
-    int k = file.getName().lastIndexOf('.');
-    if (k > 0) {
-      return file.getName().substring(k + 1).toLowerCase();
-    }
-    throw new IllegalArgumentException("Error parsing the extension");
-  }
-
-  /**
    * Joins two images into the same sequence.
    *
@@ -213,133 +185,4 @@
     if (Main.main != null)
       MapillaryData.dataUpdated();
-  }
-
-  /**
-   * Reads a JPG pictures that contains the needed GPS information (position and
-   * direction) and creates a new icon in that position.
-   *
-   * @param file The file where the picture is located.
-   * @return The imported image.
-   * @throws ImageReadException If the file isn't an image.
-   * @throws IOException        If the file doesn't have the valid metadata.
-   */
-  public static MapillaryImportedImage readJPG(File file)
-          throws IOException, ImageReadException {
-    return readJPG(file, false);
-  }
-
-  /**
-   * Reads a JPG pictures that contains the needed GPS information (position and
-   * direction) and creates a new icon in that position.
-   *
-   * @param file            The {@link File} where the picture is located.
-   * @param exceptionNoTags {@code true} if an exception must be thrown if the image doesn't
-   *                        have all the needed EXIF tags; {@code false} returns an image in
-   *                        the center of the screen.
-   * @return The imported image, whose data has been extracted from the
-   * picture's metadata.
-   * @throws ImageReadException       If the {@link File} isn't an image.
-   * @throws IOException              If the {@link File} doesn't have the valid metadata.
-   * @throws IllegalArgumentException if exceptionNoTags is set to {@code true} and the image doesn't
-   *                                  have the needed EXIF tags.
-   */
-  public static MapillaryImportedImage readJPG(File file,
-                                               boolean exceptionNoTags) throws IOException, ImageReadException {
-    final ImageMetadata metadata = Imaging.getMetadata(file);
-    if (metadata instanceof JpegImageMetadata) {
-      final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
-      final TiffField lat_ref = jpegMetadata.findEXIFValueWithExactMatch(
-              GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF);
-      final TiffField lat = jpegMetadata
-              .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LATITUDE);
-      final TiffField lon_ref = jpegMetadata.findEXIFValueWithExactMatch(
-              GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF);
-      final TiffField lon = jpegMetadata
-              .findEXIFValueWithExactMatch(GpsTagConstants.GPS_TAG_GPS_LONGITUDE);
-      final TiffField ca = jpegMetadata.findEXIFValueWithExactMatch(
-              GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
-      final TiffField datetimeOriginal = jpegMetadata
-              .findEXIFValueWithExactMatch(
-                      ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
-      if (lat_ref == null || lat == null || lon == null || lon_ref == null) {
-        if (exceptionNoTags) {
-          throw new IllegalArgumentException("The image doesn't have the needed EXIF tags.");
-        }
-        return readNoTags(file);
-      }
-      double latValue = 0;
-      double lonValue = 0;
-      double caValue = 0;
-      if (lat.getValue() instanceof RationalNumber[])
-        latValue = MapillaryUtils.degMinSecToDouble(
-                (RationalNumber[]) lat.getValue(), lat_ref.getValue().toString());
-      if (lon.getValue() instanceof RationalNumber[])
-        lonValue = MapillaryUtils.degMinSecToDouble(
-                (RationalNumber[]) lon.getValue(), lon_ref.getValue().toString());
-      if (ca != null && ca.getValue() instanceof RationalNumber)
-        caValue = ((RationalNumber) ca.getValue()).doubleValue();
-      if (datetimeOriginal != null) {
-        return new MapillaryImportedImage(new LatLon(latValue, lonValue), caValue, file, datetimeOriginal.getStringValue());
-      }
-      return new MapillaryImportedImage(new LatLon(latValue, lonValue), caValue, file);
-    }
-    throw new IllegalStateException("Invalid format.");
-  }
-
-  /**
-   * Reads a image file that doesn't contain the needed GPS information. And
-   * creates a new icon in the middle of the map.
-   *
-   * @param file The file where the image is located.
-   * @return The imported image.
-   */
-  public static MapillaryImportedImage readNoTags(File file) {
-    return readNoTags(file, Main.map.mapView.getProjection()
-            .eastNorth2latlon(Main.map.mapView.getCenter()));
-  }
-
-  /**
-   * Reads a image file that doesn't contain the needed GPS information. And
-   * creates a new icon in the middle of the map.
-   *
-   * @param file The file where the image is located.
-   * @param pos  A {@link LatLon} object indicating the position in the map where
-   *             the image must be set.
-   * @return The imported image.
-   */
-  public static MapillaryImportedImage readNoTags(File file, LatLon pos) {
-    ImageMetadata metadata = null;
-    try {
-      metadata = Imaging.getMetadata(file);
-    } catch (IOException e) {
-      Main.error(e);
-    } catch (ImageReadException e) {
-      Main.error(e);
-    }
-    if (metadata instanceof JpegImageMetadata) {
-      final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
-      final TiffField datetimeOriginal = jpegMetadata
-              .findEXIFValueWithExactMatch(
-                      ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
-      if (datetimeOriginal == null) {
-        return new MapillaryImportedImage(pos, 0, file);
-      }
-      try {
-        return new MapillaryImportedImage(pos, 0, file, datetimeOriginal.getStringValue());
-      } catch (ImageReadException e) {
-        Main.error(e);
-      }
-    }
-    return new MapillaryImportedImage(pos, 0, file);
-  }
-
-  /**
-   * Reads an image in PNG format.
-   *
-   * @param file The file where the image is located.
-   * @return The imported image.
-   */
-  public static MapillaryImportedImage readPNG(File file) {
-    return readNoTags(file);
   }
 
