Android图像处理简介の图像存储和元数据

 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类型:

  1. Uri imageFileUri = getContentResolver().insert(  
  2.                         Media.EXTERNAL_CONTENT_URI, new ContentValues());  
  3. // Start the Camera App  
  4. Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);  
  5. it.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri);  
  6. startActivityForResult(it, CAMERA_RESULT);  

上面代码中的ContentValues对象是捕获的图像在创建时要关联的元数据,当然,上面的元数据是空的。我们可以使用put函数将元数据信息写入ContentValues中,ContentValues是以键值对的形式存储数据的,键名是定义在android.provider.MediaStore.Images.Media类中的常量:

  1. // Save the name and description of an image in a ContentValues map  
  2. ContentValues contentValues = new ContentValues(3);  
  3. contentValues.put(Media.DISPLAY_NAME, "ASCE1885_TITLE");  
  4. contentValues.put(Media.DESCRIPTION, "ASCE1885_DESCRIPTION");  
  5. contentValues.put(Media.MIME_TYPE, "image/jpeg");  
  6.                   
  7. // Add a new recode without the bitmap, but with some values set.  
  8. // insert() returns the URI of the new record  
  9. Uri imageFileUri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, contentValues);  


上面获取的Uri可能类似于:

content://media/external/images/media/16

这里说明一点,以content开头的Uri一般都是被content provider使用的,例如上面的Uri是被MediaStore使用的一样。

反过来根据Uri,我们可以用来检索这个Uri对应路径中的图像数据,代码如下:

  1. Bitmap bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageFileUri),null,bmpFactory);  
  2. <p> </p>  

在我们捕获图像并存放在MediaStore中后,如果还想再增加元数据信息,那么可以使用ContentResolver的update函数来实现:

  1. // Update the MediaStore record with Title and Description  
  2. ContentValues contentValues = new ContentValues(3);  
  3. contentValues.put(Media.DISPLAY_NAME, "WEN1885_TITLE");  
  4. contentValues.put(Media.DESCRIPTION, "WEN1885_DESCRIPTION");  
  5. getContentResolver().update(imageFileUri, contentValues, nullnull);  

完整的代码例子如下,先看layout/main.xml文件:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7. <ImageView  
  8.     android:id="@+id/ReturnedImageView"    
  9.     android:layout_width="wrap_content"   
  10.     android:layout_height="wrap_content"/>  
  11. <TextView   
  12.     android:layout_width="wrap_content"  
  13.     android:layout_height="wrap_content"  
  14.     android:text="Title:"  
  15.     android:id="@+id/TitleTextView" />  
  16. <EditText   
  17.     android:layout_width="fill_parent"  
  18.     android:layout_height="wrap_content"  
  19.     android:id="@+id/TitleEditText"/>  
  20. <TextView   
  21.     android:layout_width="wrap_content"  
  22.     android:layout_height="wrap_content"  
  23.     android:text="Description"  
  24.     android:id="@+id/DescriptionTextView"/>  
  25. <EditText   
  26.     android:layout_width="fill_parent"  
  27.     android:layout_height="wrap_content"  
  28.     android:id="@+id/DescriptionEditText"/>  
  29. <Button   
  30.     android:layout_width="wrap_content"  
  31.     android:layout_height="wrap_content"  
  32.     android:id="@+id/TakePictureButton"  
  33.     android:text="Take Picture"/>  
  34. <Button   
  35.     android:layout_width="wrap_content"  
  36.     android:layout_height="wrap_content"  
  37.     android:id="@+id/SaveDataButton"  
  38.     android:text="Save Data"/>  
  39. </LinearLayout>  


完整的Java代码如下:

  1. package hust.iprai.asce1885.promedia;  
  2.   
  3. import java.io.FileNotFoundException;  
  4.   
  5. import android.app.Activity;  
  6. import android.content.ContentValues;  
  7. import android.content.Intent;  
  8. import android.graphics.Bitmap;  
  9. import android.graphics.BitmapFactory;  
  10. import android.net.Uri;  
  11. import android.os.Bundle;  
  12. import android.provider.MediaStore.Images.Media;  
  13. import android.util.Log;  
  14. import android.view.View;  
  15. import android.view.View.OnClickListener;  
  16. import android.widget.Button;  
  17. import android.widget.EditText;  
  18. import android.widget.ImageView;  
  19. import android.widget.TextView;  
  20. import android.widget.Toast;  
  21.   
  22. public class MediaStoreCameraActivity extends Activity {  
  23.     final static int CAMERA_RESULT = 0;  
  24.       
  25.     Uri imageFileUri = null;  
  26.       
  27.     // User interface elements, specified in res/layout/main.xml  
  28.     ImageView returnedImageView;  
  29.     Button takePictureButton;  
  30.     Button saveDataButton;  
  31.     TextView titleTextView;  
  32.     TextView descriptionTextView;  
  33.     EditText titleEditText;  
  34.     EditText descriptionEditText;  
  35.       
  36.     @Override  
  37.     protected void onCreate(Bundle savedInstanceState) {  
  38.         super.onCreate(savedInstanceState);  
  39.           
  40.         // Set the content view to be what is defined in the res/layout/main.xml file  
  41.         setContentView(R.layout.main);  
  42.           
  43.         // Get references to UI elements  
  44.         returnedImageView = (ImageView) findViewById(R.id.ReturnedImageView);  
  45.         takePictureButton = (Button) findViewById(R.id.TakePictureButton);  
  46.         saveDataButton = (Button) findViewById(R.id.SaveDataButton);  
  47.         titleTextView = (TextView) findViewById(R.id.TitleTextView);  
  48.         descriptionTextView = (TextView) findViewById(R.id.DescriptionTextView);  
  49.         titleEditText = (EditText) findViewById(R.id.TitleEditText);  
  50.         descriptionEditText = (EditText) findViewById(R.id.DescriptionEditText);  
  51.           
  52.         // Set all except takePictureButton to not be visible initially  
  53.         // View.GONE is invisible and doesn't take up space in the layout  
  54.         returnedImageView.setVisibility(View.GONE);  
  55.         saveDataButton.setVisibility(View.GONE);  
  56.         titleTextView.setVisibility(View.GONE);  
  57.         descriptionTextView.setVisibility(View.GONE);  
  58.         titleEditText.setVisibility(View.GONE);  
  59.         descriptionEditText.setVisibility(View.GONE);  
  60.           
  61.           
  62.         // When the Take Picture Button is clicked  
  63.         takePictureButton.setOnClickListener(new OnClickListener() {  
  64.   
  65.             public void onClick(View v) {  
  66.                 // Add a new record without the bitmap  
  67.                 // return the URI of the new record  
  68.                 imageFileUri = getContentResolver().insert(  
  69.                         Media.EXTERNAL_CONTENT_URI, new ContentValues());  
  70.                 // Start the Camera App  
  71.                 Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);  
  72.                 it.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri);  
  73.                 startActivityForResult(it, CAMERA_RESULT);  
  74.             }  
  75.               
  76.         });  
  77.           
  78.         saveDataButton.setOnClickListener(new OnClickListener() {  
  79.   
  80.             public void onClick(View v) {                 
  81.                 // Update the MediaStore record with Title and Description  
  82.                 ContentValues contentValues = new ContentValues(3);  
  83.                 contentValues.put(Media.DISPLAY_NAME, titleEditText.getText().toString());  
  84.                 contentValues.put(Media.DESCRIPTION, descriptionEditText.getText().toString());  
  85.                 getContentResolver().update(imageFileUri, contentValues, nullnull);  
  86.   
  87.                 // Tell the user  
  88.                 Toast bread = Toast.makeText(MediaStoreCameraActivity.this"Record Updated", Toast.LENGTH_LONG);  
  89.                 bread.show();  
  90.                   
  91.                 // Go back to the initial state, set Take Picture Button Visible  
  92.                 // hide other UI elements  
  93.                 takePictureButton.setVisibility(View.VISIBLE);  
  94.                 returnedImageView.setVisibility(View.GONE);  
  95.                 titleTextView.setVisibility(View.GONE);  
  96.                 descriptionTextView.setVisibility(View.GONE);  
  97.                 titleEditText.setVisibility(View.GONE);  
  98.                 descriptionEditText.setVisibility(View.GONE);  
  99.             }  
  100.               
  101.         });  
  102.     }  
  103.   
  104.     @Override  
  105.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  106.         super.onActivityResult(requestCode, resultCode, data);  
  107.           
  108.         if (RESULT_OK == resultCode) {  
  109.             // The Camera App has returned  
  110.             // Hide the Take Picture Button  
  111.             takePictureButton.setVisibility(View.GONE);  
  112.               
  113.             // Show the other UI elements  
  114.             saveDataButton.setVisibility(View.VISIBLE);  
  115.             returnedImageView.setVisibility(View.VISIBLE);  
  116.             titleTextView.setVisibility(View.VISIBLE);  
  117.             descriptionTextView.setVisibility(View.VISIBLE);  
  118.             titleEditText.setVisibility(View.VISIBLE);  
  119.             descriptionEditText.setVisibility(View.VISIBLE);  
  120.               
  121.             // Scale the image  
  122.             int dw = 200// Make it at most 200 pixels wide  
  123.             int dh = 200// Make it at most 200 pixels tall  
  124.               
  125.             BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();  
  126.             bmpFactoryOptions.inJustDecodeBounds = true;  
  127.             Bitmap bmp = null;  
  128.             try {  
  129.                 bmp = BitmapFactory.decodeStream(  
  130.                         getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions);  
  131.             } catch (FileNotFoundException e) {  
  132.                 e.printStackTrace();  
  133.             }  
  134.             int heightRatio = (int) Math.ceil(bmpFactoryOptions.outHeight/(float)dh);  
  135.             int widthRatio = (int) Math.ceil(bmpFactoryOptions.outWidth/(float)dw);  
  136.               
  137.             Log.v("HEIGHTRATIO""" + heightRatio);  
  138.             Log.v("WIDTHRATIO""" + widthRatio);  
  139.               
  140.             // If both of the ratios are greater than 1  
  141.             // one of the sides of the image is greater than the screen  
  142.             if ((heightRatio > 1) && (widthRatio > 1)) {  
  143.                 if (heightRatio > widthRatio) {  
  144.                     // Height ratio is larger, scale according to it  
  145.                     bmpFactoryOptions.inSampleSize = heightRatio;  
  146.                 } else {  
  147.                     // Width ratio is larger, scale according to it  
  148.                     bmpFactoryOptions.inSampleSize = widthRatio;  
  149.                 }  
  150.             }  
  151.               
  152.             // Decode it for real  
  153.             bmpFactoryOptions.inJustDecodeBounds = false;  
  154.             try {  
  155.                 bmp = BitmapFactory.decodeStream(  
  156.                         getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions);  
  157.             } catch (FileNotFoundException e) {  
  158.                 e.printStackTrace();  
  159.                 Log.v("ERROR", e.toString());  
  160.             }  
  161.               
  162.             // Display it  
  163.             returnedImageView.setImageBitmap(bmp);  
  164.         }  
  165.     }  
  166. }  


2)使用MediaStore来检索图像数据

MediaStore,跟所有的content provider一样使用类似于数据库操作的方式来检索数据。从指定的Uri中选择数据记录,之后通过Cursor对象来对结果进行迭代处理。

首先需要创建一个字符串数组来表示希望返回的列类型,MediaStore中图像数据的标准列类型在MediaStore.Images.Media类中:

  1. String[] columns =   
  2.     {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME};  


执行实际的查询操作使用Activity的managedQuery函数,第一个参数是URI,第二个参数是列名组成的字符串数组,第三个参数是WHERE语句,后面跟的参数是WHERE包含的参数,最后一个参数是ORDER BY语句:

  1. long oneHourAgo = System.currentTimeMillis()/1000 - (60*60);  
  2. String[] whereValues = {"" + oneHourAgo};  
  3. // 指定返回结果的列  
  4. String[] columns = {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME, Media.DATE_ADDED};  
  5. // 获得游标  
  6. Cursor cursor = managedQuery(Media.EXTERNAL_CONTENT_URI, columns, Media.DATE_ADDED + " > ?",whereValues, Media.DATE_ADDED + " ASC");  
  7. // 返回指定列的索引  
  8. int displayColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);  
  9. // 移到游标的开始处  
  10. if (cursor.moveToFirst()) {  
  11.     String displayName = cursor.getString(displayColumnIndex);  
  12. }  


完整的例子如下所示,先是layout/main.xml文件:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7. <ImageButton   
  8.     android:layout_width="wrap_content"  
  9.     android:layout_height="wrap_content"  
  10.     android:id="@+id/ImageButton"/>  
  11. <TextView   
  12.     android:layout_width="fill_parent"  
  13.     android:layout_height="wrap_content"  
  14.     android:id="@+id/TitleTextView"  
  15.     android:text="Image Title"/>  
  16. </LinearLayout>  


Java代码如下:

  1. package hust.iprai.asce1885.promedia;  
  2.   
  3. import android.app.Activity;  
  4. import android.database.Cursor;  
  5. import android.graphics.Bitmap;  
  6. import android.graphics.BitmapFactory;  
  7. import android.os.Bundle;  
  8. import android.provider.MediaStore;  
  9. import android.provider.MediaStore.Images.Media;  
  10. import android.util.Log;  
  11. import android.view.View;  
  12. import android.view.View.OnClickListener;  
  13. import android.widget.ImageButton;  
  14. import android.widget.TextView;  
  15.   
  16. public class MediaStoreGallery extends Activity {  
  17.   
  18.     public final static int DISPLAYWIDTH = 200;  
  19.     public final static int DISPLAYHEIGHT = 200;  
  20.       
  21.     TextView titleTextView;  
  22.     ImageButton imageButton;  
  23.       
  24.     Cursor cursor;  
  25.     Bitmap bmp;  
  26.     String imageFilePath;  
  27.     int fileColumn;  
  28.     int titleColumn;  
  29.     int displayColumn;  
  30.       
  31.     @Override  
  32.     protected void onCreate(Bundle savedInstanceState) {  
  33.         super.onCreate(savedInstanceState);  
  34.         setContentView(R.layout.main);  
  35.           
  36.         titleTextView = (TextView) findViewById(R.id.TitleTextView);  
  37.         imageButton = (ImageButton) findViewById(R.id.ImageButton);  
  38.           
  39.         String[] columns = {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME};  
  40.         cursor = managedQuery(Media.EXTERNAL_CONTENT_URI, columns, nullnullnull);  
  41.           
  42.         // 注意:Media.DATA是MediaStore.Images.Media.DATA的缩写  
  43.         fileColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);  
  44.         titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE);  
  45.         displayColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME);  
  46.           
  47.         if (cursor.moveToFirst()) {  
  48.             titleTextView.setText(cursor.getString(titleColumn));  
  49.               
  50.             imageFilePath = cursor.getString(fileColumn);  
  51.             bmp = getBitmap(imageFilePath);  
  52.               
  53.             // Display it  
  54.             imageButton.setImageBitmap(bmp);  
  55.         }  
  56.           
  57.         imageButton.setOnClickListener(new OnClickListener() {  
  58.   
  59.             public void onClick(View v) {  
  60.                 if (cursor.moveToNext()) {  
  61.                     titleTextView.setText(cursor.getString(displayColumn));  
  62.                       
  63.                     imageFilePath = cursor.getString(fileColumn);  
  64.                     bmp = getBitmap(imageFilePath);  
  65.                     imageButton.setImageBitmap(bmp);  
  66.                 }  
  67.             }  
  68.               
  69.         });  
  70.     }  
  71.       
  72.     private Bitmap getBitmap(String imageFilePath) {  
  73.         // Load up the image's dimensions not the image itself  
  74.         BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();  
  75.         bmpFactoryOptions.inJustDecodeBounds = true;  
  76.         Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);  
  77.           
  78.         int heightRatio = (int) Math.ceil(bmpFactoryOptions.outHeight/(float)DISPLAYHEIGHT);  
  79.         int widthRatio = (int) Math.ceil(bmpFactoryOptions.outWidth/(float)DISPLAYWIDTH);  
  80.           
  81.         Log.v("HEIGHTRATIO""" + heightRatio);  
  82.         Log.v("WIDTHRATIO""" + widthRatio);  
  83.           
  84.         // If both of the ratios are greater than 1, one of the sides of   
  85.         // the image is greater than the screen  
  86.         if ((heightRatio > 1) && (widthRatio > 1)) {  
  87.             if (heightRatio > widthRatio) {  
  88.                 bmpFactoryOptions.inSampleSize = heightRatio;  
  89.             } else {  
  90.                 bmpFactoryOptions.inSampleSize = widthRatio;  
  91.             }  
  92.         }  
  93.           
  94.         // Decode it for real  
  95.         bmpFactoryOptions.inJustDecodeBounds = false;  
  96.         bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);  
  97.           
  98.         return bmp;  
  99.     }  
  100. }  


2)内部元数据

EXIF,可交换图像文件格式(Exchangeable Image File Format),是将元数据保存到图像文件里的标准格式。它的数据存储与JPEG格式是完全相同的,它就是在JPEG格式头部插入了数码照片的拍摄信息。

EXIF数据中包含很多与图像拍摄紧密相关的技术参数,例如曝光时间ExposureTime和快门速度ShutterSpeedValue等。还有一些参数是我们可以在后续进行填充或修改的,例如:

UserComment: 用户评论

ImageDescription:图像的描述

Artist:图像的创建者或者拍摄者

Copyright:版权

Software:创建图像使用的软件

Android提供了方便的接口ExifInterface来读写EXIF数据:

  1. ExifInterface ei = new ExifInterface(imageFilePath);  
  2. String imageDescription = ei.getAttribute("ImageDescription");  
  3. if (null != imageDescription) {  
  4.         Log.v("EXIF", imageDescription);  
  5. }  


保存EXIF数据到图像文件中的代码片段如下:

  1. ExifInterface ei = new ExifInterface(imageFilePath);  
  2. ei.setAttribute("ImageDescription""ASCE1885");  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值