【问题标题】:Write/Geotag JPEGs (EXIF data) in Android在 Android 中写入/地理标记 JPEG(EXIF 数据)
【发布时间】:2012-05-10 09:51:37
【问题描述】:

我想做什么:
使用我自己的 PictureActivity* 拍照并添加 EXIF(地理标签)数据
*:实现SurfaceHolder.Callback并使用Camera

什么不工作:
添加 EXIF GPS 数据

我尝试过的:
使用ExifInterface和手动设置Camera.Parameters(都使用设置GPS元数据的具体方法和使用params.set(String, Value))。

我正在使用 FlickrJ 将图片上传到 Flickr(是的,我已将 Flickr 设置为导入 GPS 数据——其他图片工作正常),但是此工具还显示 EXIF 中没有 GPS 数据:http://regex.info/exif.cgi

我错过了什么?

(Android 2.2,HTC Desire)

编辑:
- 相机设置为Geotag photos: On
- 我尝试过硬编码的虚拟 GPS 位置

这里是手动设置参数的代码(先删除和不删除 GPS 数据都试过,set(String, Value) 也提到过):

@Override
public void surfaceCreated(SurfaceHolder holder) {
    mCamera = Camera.open();    

    Camera.Parameters p = mCamera.getParameters();
    p.setPreviewSize(p.getPreviewSize().width, p.getPreviewSize().height);
    Log.e("PictureActivity", "EXIF: "+AGlanceLocationListener.getLatitude());
    p.removeGpsData();
    p.setGpsLatitude( AGlanceLocationListener.getLatitude() );
    p.setGpsLongitude( AGlanceLocationListener.getLongitude() );
    p.setGpsAltitude( AGlanceLocationListener.getAltitude() );
    p.setGpsTimestamp( AGlanceLocationListener.getTime() );
    mCamera.setParameters(p);
}

这里是使用ExifInterface的代码:

//Save EXIF location data to JPEG
ExifInterface exif;
try {
    exif = new ExifInterface("/sdcard/DCIM/"+filename+".jpeg");
    exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE,
        String.valueOf(AGlanceLocationListener.getLatitude()));

    exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, 
        String.valueOf(AGlanceLocationListener.getLongitude()));

    exif.saveAttributes();

} catch (IOException e) {
    Log.e("PictureActivity", e.getLocalizedMessage());
}

这是将 JPEG 文件写入 SDCARD 的代码:

Camera.PictureCallback jpegCallback = new Camera.PictureCallback() {
    public void onPictureTaken(byte[] imageData, Camera c) 
    {
        //      Bitmap pic = BitmapFactory.decodeByteArray(imageData, 0, imageData.length);

        String day = String.valueOf(Calendar.getInstance().getTime().getDay());
        String hour = String.valueOf(Calendar.getInstance().getTime().getHours());
        String minute = String.valueOf(Calendar.getInstance().getTime().getMinutes());
        String second = String.valueOf(Calendar.getInstance().getTime().getSeconds());

        filename = "Billede"+day+hour+minute+second;

        try {
            FileOutputStream fos = new FileOutputStream(new File("/sdcard/DCIM/"+filename+".jpeg"));
            fos.write(imageData);
            fos.flush();
            fos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

        if(imageData != null){
            Intent mIntent = new Intent();
            setResult(0,mIntent);
            PictureActivity.this.showDialog(0);
        }
    }
};

还尝试从Bitmap 写入图像(没有工作),以及此处的另一个问题报告使用FileOutputStream 编写的工作

【问题讨论】:

    标签: android exif


    【解决方案1】:

    不幸的是,这仅适用于半球的四分之一。格林威治以东和赤道以北。这就是我猜你必须住在那里的方式:)。您的“Math.floor”将使所有负值错误(例如 -105 变为 -106)。这是同样的事情,即使在美国也应该有效。

    public void loc2Exif(String flNm, Location loc) {
      try {
        ExifInterface ef = new ExifInterface(flNm);
        ef.setAttribute(ExifInterface.TAG_GPS_LATITUDE, dec2DMS(loc.getLatitude()));
        ef.setAttribute(ExifInterface.TAG_GPS_LONGITUDE,dec2DMS(loc.getLongitude()));
        if (loc.getLatitude() > 0) 
          ef.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "N"); 
        else              
          ef.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "S");
        if (loc.getLongitude()>0) 
          ef.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "E");    
         else             
           ef.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "W");
        ef.saveAttributes();
      } catch (IOException e) {}         
    }
    //-----------------------------------------------------------------------------------
    String dec2DMS(double coord) {  
      coord = coord > 0 ? coord : -coord;  // -105.9876543 -> 105.9876543
      String sOut = Integer.toString((int)coord) + "/1,";   // 105/1,
      coord = (coord % 1) * 60;         // .987654321 * 60 = 59.259258
      sOut = sOut + Integer.toString((int)coord) + "/1,";   // 105/1,59/1,
      coord = (coord % 1) * 60000;             // .259258 * 60000 = 15555
      sOut = sOut + Integer.toString((int)coord) + "/1000";   // 105/1,59/1,15555/1000
      return sOut;
    }
    

    ...一旦你让我开始,这就是相反的

    public Location exif2Loc(String flNm) {
      String sLat = "", sLatR = "", sLon = "", sLonR = "";
      try {
        ExifInterface ef = new ExifInterface(flNm);
        sLat  = ef.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
        sLon  = ef.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
        sLatR = ef.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
        sLonR = ef.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
      } catch (IOException e) {return null;}
    
      double lat = dms2Dbl(sLat);
      if (lat > 180.0) return null; 
      double lon = dms2Dbl(sLon);
      if (lon > 180.0) return null; 
    
      lat = sLatR.contains("S") ? -lat : lat;
      lon = sLonR.contains("W") ? -lon : lon;
    
      Location loc = new Location("exif");
      loc.setLatitude(lat);
      loc.setLongitude(lon);
      return loc;
    }
    //-------------------------------------------------------------------------
    double dms2Dbl(String sDMS){
      double dRV = 999.0;
      try {
        String[] DMSs = sDMS.split(",", 3);
        String s[] = DMSs[0].split("/", 2);
        dRV = (new Double(s[0])/new Double(s[1]));
        s = DMSs[1].split("/", 2);
        dRV += ((new Double(s[0])/new Double(s[1]))/60);
        s = DMSs[2].split("/", 2);
        dRV += ((new Double(s[0])/new Double(s[1]))/3600);
      } catch (Exception e) {}
      return dRV;
    }
    

    ...总有一天,我会开始编写漂亮的代码。快乐的地理标记,肖恩

    【讨论】:

      【解决方案2】:

      发现问题:

      查看 SDCARD 中的原始图像显示:

      1. 如果使用 EXIFInterface,图像包含 EXIF GPS 数据。然而,GPS 数据是错误的(见下文)——这可能是 Flickr 不会显示它的原因。

      2. 使用通过相机参数设置 GPS 坐标的方法不会写入 EXIF GPS 数据(这是使用虚拟硬编码坐标,我尚未使用实际 GPS 修复进行测试)。我还没有深入研究为什么会这样。

      EXIFInterface 的 Android API 有 this documentation:

      公共静态最终字符串 TAG_GPS_LONGITUDE
      自:API 5 级
      细绳。格式为“num1/denom1,num2/denom2,num3/denom3”。
      常量值:“GPSLongitude”

      我的原始代码的问题是我以十进制度数传递 GPS 坐标——通过对 Location 对象调用 getLatitude/getLogitude 获得的坐标是十进制度数。 EXIFInterface 需要以度分秒为单位的坐标,然后写为有理数(这是 EXIF 规范的一部分)。更多关于 GPS 坐标格式和转换here.

      Here 是另一个问题/答案,它解释了如何从十进制度转换为度分秒。

      使用此代码,GPS 坐标在 EXIF 中正确写入,Flickr 导入数据没有问题:

      ExifInterface exif;
      
      double latitude = AGlanceLocationListener.getLatitude();
      double longitude = AGlanceLocationListener.getLongitude();
      
      try {
          exif = new ExifInterface("/sdcard/DCIM/"+filename+".jpeg");
          int num1Lat = (int)Math.floor(latitude);
          int num2Lat = (int)Math.floor((latitude - num1Lat) * 60);
          double num3Lat = (latitude - ((double)num1Lat+((double)num2Lat/60))) * 3600000;
      
          int num1Lon = (int)Math.floor(longitude);
          int num2Lon = (int)Math.floor((longitude - num1Lon) * 60);
          double num3Lon = (longitude - ((double)num1Lon+((double)num2Lon/60))) * 3600000;
      
          exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, num1Lat+"/1,"+num2Lat+"/1,"+num3Lat+"/1000");
          exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, num1Lon+"/1,"+num2Lon+"/1,"+num3Lon+"/1000");
      
      
          if (latitude > 0) {
              exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "N"); 
          } else {
              exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "S");
          }
      
          if (longitude > 0) {
              exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "E");    
          } else {
          exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "W");
          }
      
          exif.saveAttributes();
      
      } catch (IOException e) {
          Log.e("PictureActivity", e.getLocalizedMessage());
      }   
      

      注意:使用度分秒时,您还需要设置 GPS 参考属性(N、S、E、W)。

      【讨论】:

      • 对于负纬度和经度,上面的代码将编码负值而不是正值。例如 (-40.00, -73.00) 将被编码为南纬 -40 度,而不是南纬 +40 度。 -73 将被编码为西经 -73 度,而不是西经 +73 度)。
      • 通常不应将其视为正确答案,因为代码存在缺陷(math.floor 问题等...)
      【解决方案3】:

      此解决方案将满足负和正 lat/lng 值:

      static public boolean setGeoTag(File image, LatLng geoTag) {
          if (geoTag != null) {
              try {
                  ExifInterface exif = new ExifInterface(
                          image.getAbsolutePath());
      
                  double latitude = Math.abs(geoTag.latitude);
                  double longitude = Math.abs(geoTag.longitude);
      
                  int num1Lat = (int) Math.floor(latitude);
                  int num2Lat = (int) Math.floor((latitude - num1Lat) * 60);
                  double num3Lat = (latitude - ((double) num1Lat + ((double) num2Lat / 60))) * 3600000;
      
                  int num1Lon = (int) Math.floor(longitude);
                  int num2Lon = (int) Math.floor((longitude - num1Lon) * 60);
                  double num3Lon = (longitude - ((double) num1Lon + ((double) num2Lon / 60))) * 3600000;
      
                  String lat = num1Lat + "/1," + num2Lat + "/1," + num3Lat + "/1000";
                  String lon = num1Lon + "/1," + num2Lon + "/1," + num3Lon + "/1000";
      
                  if (geoTag.latitude > 0) {
                      exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "N");
                  } else {
                      exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "S");
                  }
      
                  if (geoTag.longitude > 0) {
                      exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "E");
                  } else {
                      exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "W");
                  }
      
                  exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, lat);
                  exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, lon);
      
                  exif.saveAttributes();
      
              } catch (IOException e) {
                  e.printStackTrace();
                  return false;
              }
          } else {
              return false;
          }
          return true;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-23
        • 2015-02-28
        • 1970-01-01
        • 1970-01-01
        • 2021-12-27
        • 1970-01-01
        相关资源
        最近更新 更多