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

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");



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值