频道栏目
首页 > 程序开发 > 移动开发 > Android > 正文
Android图像处理简介の图像存储和元数据
2012-02-11 11:44:18           
收藏   我要投稿

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类型:
[java]
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类中的常量:
[java]
// 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对应路径中的图像数据,代码如下:
[java]
Bitmap bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageFileUri),null,bmpFactory); 
<p> </p> 
在我们捕获图像并存放在MediaStore中后,如果还想再增加元数据信息,那么可以使用ContentResolver的update函数来实现:
[java]
// 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文件:
[html]
<?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代码如下:
[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类中:
[java]
String[] columns =  
    {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME}; 

执行实际的查询操作使用Activity的managedQuery函数,第一个参数是URI,第二个参数是列名组成的字符串数组,第三个参数是WHERE语句,后面跟的参数是WHERE包含的参数,最后一个参数是ORDER BY语句:
[java]
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文件:
[html]
<?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代码如下:
[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数据:
[java]
ExifInterface ei = new ExifInterface(imageFilePath); 
String imageDescription = ei.getAttribute("ImageDescription"); 
if (null != imageDescription) { 
        Log.v("EXIF", imageDescription); 

保存EXIF数据到图像文件中的代码片段如下:
[java]
ExifInterface ei = new ExifInterface(imageFilePath); 
ei.setAttribute("ImageDescription", "ASCE1885"); 


摘自 ASCE1885的专栏

点击复制链接 与好友分享!回本站首页
上一篇:访问Android硬件资源の管理网络和Wifi连接
下一篇:Android图像处理简介の使用内置Camera应用程序进行图像捕获
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站