在 Android 中写入/地理标记 JPEG(EXIF 数据)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10531544/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Write/Geotag JPEGs (EXIF data) in Android
提问by MSurrow
What I want to do:
Take a picture using my own PictureActivity* and add EXIF (geotags) data
*: Implementing SurfaceHolder.Callback
and using Camera
我想做什么:
使用我自己的 PictureActivity* 拍照并添加 EXIF(地理标签)数据
*:实施SurfaceHolder.Callback
和使用Camera
What is not working:
Adding the EXIF GPS data
什么不起作用:
添加 EXIF GPS 数据
What I've tried:
Using the ExifInterface
and manually setting Camera.Parameters
(both with the specific methods for setting GPS meta-data and by using params.set(String, Value)
).
我尝试过的:
使用ExifInterface
和 手动设置Camera.Parameters
(均使用设置 GPS 元数据的特定方法和使用params.set(String, Value)
)。
I'm uploading the pictures to Flickr using FlickrJ (yes, I've set Flickr to import GPS data -- other pictures work fine), however this tool also says there is no GPS data in the EXIF: http://regex.info/exif.cgi
我正在使用 FlickrJ 将图片上传到 Flickr(是的,我已将 Flickr 设置为导入 GPS 数据——其他图片工作正常),但是此工具还表示 EXIF 中没有 GPS 数据:http://regex。信息/exif.cgi
What am I missing?
我错过了什么?
(Android 2.2, HTC Desire)
(Android 2.2, HTC Desire)
Edit:
- The camera is set to Geotag photos: On
- I've tried with hardcoded dummy GPS positions
编辑:
- 相机设置为Geotag photos: On
- 我试过硬编码的虚拟 GPS 位置
Here is the code for manually setting parameters (tried both with and without first removing the GPS data, and as mentioned also with set(String, Value)
):
以下是手动设置参数的代码(在先删除 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);
}
Here is the code for using the ExifInterface
:
这是使用 的代码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());
}
Here is the code for writing the JPEG file to the SDCARD:
这是将 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);
}
}
};
Also tried writing the image from a Bitmap
(didn't work), plus another question here report writing using a FileOutputStream
worked
还尝试从一个Bitmap
(不起作用)写图像,加上另一个问题在这里报告使用FileOutputStream
工作
回答by seanpj
Unfortunately, this works on a quarter of the hemisphere only. East of Greenwich and North of the equator. That's how I guessed you must live there:). Your 'Math.floor' will make all negative values wrong (like -105 into -106). Here's the same thing, that should work even in the US.
不幸的是,这只适用于半球的四分之一。格林威治以东和赤道以北。这就是我猜你必须住在那里的方式:)。您的“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;
}
... and once you got me started, here's the reverse
...一旦你让我开始,这里是相反的
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;
}
... and one day, I'll start to write pretty looking code. Happy geotagging, sean
...总有一天,我将开始编写漂亮的代码。快乐的地理标记,肖恩
回答by MSurrow
Found the problem:
发现问题:
Looking at the original images from the SDCARD showed:
查看来自 SDCARD 的原始图像显示:
The images contained EXIF GPS data if the EXIFInterface is used. The GPS data, however, was wrong (see below) -- which probably is why Flickr won't show it.
Using the method that sets GPS coordinates through the camera parameters do NOT write EXIF GPS data (this was using dummy hardcoded coordinates, I haven't tested with an actual GPS fix). I haven't looked more into why this is.
如果使用 EXIFInterface,则图像包含 EXIF GPS 数据。然而,GPS 数据是错误的(见下文)——这可能就是 Flickr 不会显示它的原因。
使用通过相机参数设置 GPS 坐标的方法不写入 EXIF GPS 数据(这是使用虚拟硬编码坐标,我没有使用实际的 GPS 修复进行测试)。我没有更多地研究这是为什么。
The Android API for EXIFInterface has this documentation:
EXIFInterface 的 Android API 有这个文档:
public static final String TAG_GPS_LONGITUDE
Since: API Level 5
String. Format is "num1/denom1,num2/denom2,num3/denom3".
Constant Value: "GPSLongitude"
public static final String TAG_GPS_LONGITUDE
自:API 级别 5
字符串。格式为“num1/denom1,num2/denom2,num3/denom3”。
常数值:“GPS经度”
The problem with my original code is that I was passing the GPS coordinates in Decimal Degrees -- the coordinates you get from calling getLatitude/getLogitude on a Location object is in Decimal Degrees. The EXIFInterface expects the coordinates in Degrees Minutes Seconds and then written as rationals (this is part of the EXIF specification). More on GPS coordinate formats and conversion here.
我的原始代码的问题是我以十进制传递 GPS 坐标——您通过在 Location 对象上调用 getLatitude/getLogitude 获得的坐标以十进制表示。EXIFInterface 需要以度分秒为单位的坐标,然后写为有理数(这是 EXIF 规范的一部分)。有关 GPS 坐标格式和转换的更多信息,请点击此处。
Hereis another question/answer that explains how to convert from Decimal Degrees to Degrees Minutes Seconds.
这是另一个问题/答案,解释了如何从十进制度转换为度分秒。
Using this code, the GPS coordinates gets written correctly in the EXIF and Flickr have no problem importing the data:
使用此代码,在 EXIF 中正确写入 GPS 坐标,并且 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());
}
Note: When using Degrees Minutes Seconds you also need to set the GPS reference attributes (N, S, E, W).
注意:使用度分秒时,您还需要设置 GPS 参考属性(N、S、E、W)。
回答by Werner
This solution will cater for negative and positive lat/lng values:
此解决方案将满足负和正 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;
}