Android图像处理简介の图像存储和元数据
2011-12-18 18:39
351 查看
Android提供Content Provider来实现应用程序之间的数据共享,provider提供了标准的接口用于存储和检索多种类型的数据。图像 、音频和视频的标准content provider就是MediaStore。
1)获取图像的URI
要获得标准的图像存储路径,我们需要获得MediaStore的引用,而这是通过content resolver来实现的(因为使用Content resolver可以获取content provider,而MediaStore就是一个content provider)。
传递指定的URI给content resolver,可以得到对应的content provider,由于是新增一张图像,所以使用insert方法,相应的URI是android.provider.MediaStore.Images.Media类定义的常量EXTERNAL_CONTENT_URI。这个常量说明我们要将图像存储到主外部存储器中,通常就是SD卡;如果要将图像存储到设备内存中,则使用INTERNAL_CONTENT_URI。当然对于媒体文件的存储而言,由于尺寸一般都比较大,因此会优先考虑使用EXTERNAL_CONTENT_URI。
Content resolver类的insert函数返回值是URI类型:
上面代码中的ContentValues对象是捕获的图像在创建时要关联的元数据,当然,上面的元数据是空的。我们可以使用put函数将元数据信息写入ContentValues中,ContentValues是以键值对的形式存储数据的,键名是定义在android.provider.MediaStore.Images.Media类中的常量:
上面获取的Uri可能类似于:
content://media/external/images/media/16
这里说明一点,以content开头的Uri一般都是被content provider使用的,例如上面的Uri是被MediaStore使用的一样。
反过来根据Uri,我们可以用来检索这个Uri对应路径中的图像数据,代码如下:
在我们捕获图像并存放在MediaStore中后,如果还想再增加元数据信息,那么可以使用ContentResolver的update函数来实现:
完整的代码例子如下,先看layout/main.xml文件:
完整的Java代码如下:
2)使用MediaStore来检索图像数据
MediaStore,跟所有的content provider一样使用类似于数据库操作的方式来检索数据。从指定的Uri中选择数据记录,之后通过Cursor对象来对结果进行迭代处理。
首先需要创建一个字符串数组来表示希望返回的列类型,MediaStore中图像数据的标准列类型在MediaStore.Images.Media类中:
执行实际的查询操作使用Activity的managedQuery函数,第一个参数是URI,第二个参数是列名组成的字符串数组,第三个参数是WHERE语句,后面跟的参数是WHERE包含的参数,最后一个参数是ORDER BY语句:
完整的例子如下所示,先是layout/main.xml文件:
Java代码如下:
2)内部元数据
EXIF,可交换图像文件格式(Exchangeable Image File Format),是将元数据保存到图像文件里的标准格式。它的数据存储与JPEG格式是完全相同的,它就是在JPEG格式头部插入了数码照片的拍摄信息。
EXIF数据中包含很多与图像拍摄紧密相关的技术参数,例如曝光时间ExposureTime和快门速度ShutterSpeedValue等。还有一些参数是我们可以在后续进行填充或修改的,例如:
UserComment: 用户评论
ImageDescription:图像的描述
Artist:图像的创建者或者拍摄者
Copyright:版权
Software:创建图像使用的软件
Android提供了方便的接口ExifInterface来读写EXIF数据:
保存EXIF数据到图像文件中的代码片段如下:
1)获取图像的URI
要获得标准的图像存储路径,我们需要获得MediaStore的引用,而这是通过content resolver来实现的(因为使用Content resolver可以获取content provider,而MediaStore就是一个content provider)。
传递指定的URI给content resolver,可以得到对应的content provider,由于是新增一张图像,所以使用insert方法,相应的URI是android.provider.MediaStore.Images.Media类定义的常量EXTERNAL_CONTENT_URI。这个常量说明我们要将图像存储到主外部存储器中,通常就是SD卡;如果要将图像存储到设备内存中,则使用INTERNAL_CONTENT_URI。当然对于媒体文件的存储而言,由于尺寸一般都比较大,因此会优先考虑使用EXTERNAL_CONTENT_URI。
Content resolver类的insert函数返回值是URI类型:
Uri imageFileUri = getContentResolver().insert( Media.EXTERNAL_CONTENT_URI, new ContentValues()); // Start the Camera App Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); it.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri); startActivityForResult(it, CAMERA_RESULT);
上面代码中的ContentValues对象是捕获的图像在创建时要关联的元数据,当然,上面的元数据是空的。我们可以使用put函数将元数据信息写入ContentValues中,ContentValues是以键值对的形式存储数据的,键名是定义在android.provider.MediaStore.Images.Media类中的常量:
// Save the name and description of an image in a ContentValues map ContentValues contentValues = new ContentValues(3); contentValues.put(Media.DISPLAY_NAME, "ASCE1885_TITLE"); contentValues.put(Media.DESCRIPTION, "ASCE1885_DESCRIPTION"); contentValues.put(Media.MIME_TYPE, "image/jpeg"); // Add a new recode without the bitmap, but with some values set. // insert() returns the URI of the new record Uri imageFileUri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, contentValues);
上面获取的Uri可能类似于:
content://media/external/images/media/16
这里说明一点,以content开头的Uri一般都是被content provider使用的,例如上面的Uri是被MediaStore使用的一样。
反过来根据Uri,我们可以用来检索这个Uri对应路径中的图像数据,代码如下:
Bitmap bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageFileUri),null,bmpFactory);
在我们捕获图像并存放在MediaStore中后,如果还想再增加元数据信息,那么可以使用ContentResolver的update函数来实现:
// Update the MediaStore record with Title and Description ContentValues contentValues = new ContentValues(3); contentValues.put(Media.DISPLAY_NAME, "WEN1885_TITLE"); contentValues.put(Media.DESCRIPTION, "WEN1885_DESCRIPTION"); getContentResolver().update(imageFileUri, contentValues, null, null);
完整的代码例子如下,先看layout/main.xml文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ImageView android:id="@+id/ReturnedImageView" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Title:" android:id="@+id/TitleTextView" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/TitleEditText"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Description" android:id="@+id/DescriptionTextView"/> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/DescriptionEditText"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/TakePictureButton" android:text="Take Picture"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/SaveDataButton" android:text="Save Data"/> </LinearLayout>
完整的Java代码如下:
package hust.iprai.asce1885.promedia; import java.io.FileNotFoundException; import android.app.Activity; import android.content.ContentValues; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore.Images.Media; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; public class MediaStoreCameraActivity extends Activity { final static int CAMERA_RESULT = 0; Uri imageFileUri = null; // User interface elements, specified in res/layout/main.xml ImageView returnedImageView; Button takePictureButton; Button saveDataButton; TextView titleTextView; TextView descriptionTextView; EditText titleEditText; EditText descriptionEditText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Set the content view to be what is defined in the res/layout/main.xml file setContentView(R.layout.main); // Get references to UI elements returnedImageView = (ImageView) findViewById(R.id.ReturnedImageView); takePictureButton = (Button) findViewById(R.id.TakePictureButton); saveDataButton = (Button) findViewById(R.id.SaveDataButton); titleTextView = (TextView) findViewById(R.id.TitleTextView); descriptionTextView = (TextView) findViewById(R.id.DescriptionTextView); titleEditText = (EditText) findViewById(R.id.TitleEditText); descriptionEditText = (EditText) findViewById(R.id.DescriptionEditText); // Set all except takePictureButton to not be visible initially // View.GONE is invisible and doesn't take up space in the layout returnedImageView.setVisibility(View.GONE); saveDataButton.setVisibility(View.GONE); titleTextView.setVisibility(View.GONE); descriptionTextView.setVisibility(View.GONE); titleEditText.setVisibility(View.GONE); descriptionEditText.setVisibility(View.GONE); // When the Take Picture Button is clicked takePictureButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { // Add a new record without the bitmap // return the URI of the new record imageFileUri = getContentResolver().insert( Media.EXTERNAL_CONTENT_URI, new ContentValues()); // Start the Camera App Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); it.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri); startActivityForResult(it, CAMERA_RESULT); } }); saveDataButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { // Update the MediaStore record with Title and Description ContentValues contentValues = new ContentValues(3); contentValues.put(Media.DISPLAY_NAME, titleEditText.getText().toString()); contentValues.put(Media.DESCRIPTION, descriptionEditText.getText().toString()); getContentResolver().update(imageFileUri, contentValues, null, null); // Tell the user Toast bread = Toast.makeText(MediaStoreCameraActivity.this, "Record Updated", Toast.LENGTH_LONG); bread.show(); // Go back to the initial state, set Take Picture Button Visible // hide other UI elements takePictureButton.setVisibility(View.VISIBLE); returnedImageView.setVisibility(View.GONE); titleTextView.setVisibility(View.GONE); descriptionTextView.setVisibility(View.GONE); titleEditText.setVisibility(View.GONE); descriptionEditText.setVisibility(View.GONE); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (RESULT_OK == resultCode) { // The Camera App has returned // Hide the Take Picture Button takePictureButton.setVisibility(View.GONE); // Show the other UI elements saveDataButton.setVisibility(View.VISIBLE); returnedImageView.setVisibility(View.VISIBLE); titleTextView.setVisibility(View.VISIBLE); descriptionTextView.setVisibility(View.VISIBLE); titleEditText.setVisibility(View.VISIBLE); descriptionEditText.setVisibility(View.VISIBLE); // Scale the image int dw = 200; // Make it at most 200 pixels wide int dh = 200; // Make it at most 200 pixels tall BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options(); bmpFactoryOptions.inJustDecodeBounds = true; Bitmap bmp = null; try { bmp = BitmapFactory.decodeStream( getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions); } catch (FileNotFoundException e) { e.printStackTrace(); } int heightRatio = (int) Math.ceil(bmpFactoryOptions.outHeight/(float)dh); int widthRatio = (int) Math.ceil(bmpFactoryOptions.outWidth/(float)dw); Log.v("HEIGHTRATIO", "" + heightRatio); Log.v("WIDTHRATIO", "" + widthRatio); // If both of the ratios are greater than 1 // one of the sides of the image is greater than the screen if ((heightRatio > 1) && (widthRatio > 1)) { if (heightRatio > widthRatio) { // Height ratio is larger, scale according to it bmpFactoryOptions.inSampleSize = heightRatio; } else { // Width ratio is larger, scale according to it bmpFactoryOptions.inSampleSize = widthRatio; } } // Decode it for real bmpFactoryOptions.inJustDecodeBounds = false; try { bmp = BitmapFactory.decodeStream( getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions); } catch (FileNotFoundException e) { e.printStackTrace(); Log.v("ERROR", e.toString()); } // Display it returnedImageView.setImageBitmap(bmp); } } }
2)使用MediaStore来检索图像数据
MediaStore,跟所有的content provider一样使用类似于数据库操作的方式来检索数据。从指定的Uri中选择数据记录,之后通过Cursor对象来对结果进行迭代处理。
首先需要创建一个字符串数组来表示希望返回的列类型,MediaStore中图像数据的标准列类型在MediaStore.Images.Media类中:
String[] columns = {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME};
执行实际的查询操作使用Activity的managedQuery函数,第一个参数是URI,第二个参数是列名组成的字符串数组,第三个参数是WHERE语句,后面跟的参数是WHERE包含的参数,最后一个参数是ORDER BY语句:
long oneHourAgo = System.currentTimeMillis()/1000 - (60*60); String[] whereValues = {"" + oneHourAgo}; // 指定返回结果的列 String[] columns = {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME, Media.DATE_ADDED}; // 获得游标 Cursor cursor = managedQuery(Media.EXTERNAL_CONTENT_URI, columns, Media.DATE_ADDED + " > ?",whereValues, Media.DATE_ADDED + " ASC"); // 返回指定列的索引 int displayColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); // 移到游标的开始处 if (cursor.moveToFirst()) { String displayName = cursor.getString(displayColumnIndex); }
完整的例子如下所示,先是layout/main.xml文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/ImageButton"/> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/TitleTextView" android:text="Image Title"/> </LinearLayout>
Java代码如下:
package hust.iprai.asce1885.promedia; import android.app.Activity; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.provider.MediaStore; import android.provider.MediaStore.Images.Media; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageButton; import android.widget.TextView; public class MediaStoreGallery extends Activity { public final static int DISPLAYWIDTH = 200; public final static int DISPLAYHEIGHT = 200; TextView titleTextView; ImageButton imageButton; Cursor cursor; Bitmap bmp; String imageFilePath; int fileColumn; int titleColumn; int displayColumn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); titleTextView = (TextView) findViewById(R.id.TitleTextView); imageButton = (ImageButton) findViewById(R.id.ImageButton); String[] columns = {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME}; cursor = managedQuery(Media.EXTERNAL_CONTENT_URI, columns, null, null, null); // 注意:Media.DATA是MediaStore.Images.Media.DATA的缩写 fileColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE); displayColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME); if (cursor.moveToFirst()) { titleTextView.setText(cursor.getString(titleColumn)); imageFilePath = cursor.getString(fileColumn); bmp = getBitmap(imageFilePath); // Display it imageButton.setImageBitmap(bmp); } imageButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { if (cursor.moveToNext()) { titleTextView.setText(cursor.getString(displayColumn)); imageFilePath = cursor.getString(fileColumn); bmp = getBitmap(imageFilePath); imageButton.setImageBitmap(bmp); } } }); } private Bitmap getBitmap(String imageFilePath) { // Load up the image's dimensions not the image itself BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options(); bmpFactoryOptions.inJustDecodeBounds = true; Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions); int heightRatio = (int) Math.ceil(bmpFactoryOptions.outHeight/(float)DISPLAYHEIGHT); int widthRatio = (int) Math.ceil(bmpFactoryOptions.outWidth/(float)DISPLAYWIDTH); Log.v("HEIGHTRATIO", "" + heightRatio); Log.v("WIDTHRATIO", "" + widthRatio); // If both of the ratios are greater than 1, one of the sides of // the image is greater than the screen if ((heightRatio > 1) && (widthRatio > 1)) { if (heightRatio > widthRatio) { bmpFactoryOptions.inSampleSize = heightRatio; } else { bmpFactoryOptions.inSampleSize = widthRatio; } } // Decode it for real bmpFactoryOptions.inJustDecodeBounds = false; bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions); return bmp; } }
2)内部元数据
EXIF,可交换图像文件格式(Exchangeable Image File Format),是将元数据保存到图像文件里的标准格式。它的数据存储与JPEG格式是完全相同的,它就是在JPEG格式头部插入了数码照片的拍摄信息。
EXIF数据中包含很多与图像拍摄紧密相关的技术参数,例如曝光时间ExposureTime和快门速度ShutterSpeedValue等。还有一些参数是我们可以在后续进行填充或修改的,例如:
UserComment: 用户评论
ImageDescription:图像的描述
Artist:图像的创建者或者拍摄者
Copyright:版权
Software:创建图像使用的软件
Android提供了方便的接口ExifInterface来读写EXIF数据:
ExifInterface ei = new ExifInterface(imageFilePath); String imageDescription = ei.getAttribute("ImageDescription"); if (null != imageDescription) { Log.v("EXIF", imageDescription); }
保存EXIF数据到图像文件中的代码片段如下:
ExifInterface ei = new ExifInterface(imageFilePath); ei.setAttribute("ImageDescription", "ASCE1885");
相关文章推荐
- Android图像处理简介の图像存储和元数据
- Android图像处理简介の图像存储和元数据
- Android图像处理简介の使用内置Camera应用程序进行图像捕获
- Android图像处理简介の使用内置Camera应用程序进行图像捕获
- 数字图像处理,图像去噪算法简介
- 【Android进阶】(3)Android图像处理
- Android图像处理之Bitmap类(1)
- 数字图像处理成长之路16:QT中问题的解决办法androiddeployqt.exe"退出,退出代码 14
- 【Android图像处理】图像处理之-极速奔驰滤镜
- 从零学Android(十三)、Android中的数据存储方式简介
- Android的资源类型和存储方式简介-android学习之旅(五十二)
- [Android] 随时拍图像处理部分总结及源码分享
- Android图像处理之Bitmap类
- 关于android调用相机及拍照的存储和照片处理功能的全面小案例
- android中Bitmap图像处理 修改图片大小以及保存时的文件大小
- Android学习之图像的处理
- Android图形与图像处理-逐帧动画
- android图像处理系统框架
- 【Android图像处理】熔铸滤镜(效果)
- android图像处理之图像二值化