Android影像處理簡介の映像儲存和中繼資料

來源:互聯網
上載者:User
 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(<br />Media.EXTERNAL_CONTENT_URI, new ContentValues());<br />// Start the Camera App<br />Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);<br />it.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri);<br />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<br />ContentValues contentValues = new ContentValues(3);<br />contentValues.put(Media.DISPLAY_NAME, "ASCE1885_TITLE");<br />contentValues.put(Media.DESCRIPTION, "ASCE1885_DESCRIPTION");<br />contentValues.put(Media.MIME_TYPE, "image/jpeg");</p><p>// Add a new recode without the bitmap, but with some values set.<br />// insert() returns the URI of the new record<br />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);</p><p>

在我們捕獲映像並存放在MediaStore中後,如果還想再增加中繼資料資訊,那麼可以使用ContentResolver的update函數來實現:

// Update the MediaStore record with Title and Description<br />ContentValues contentValues = new ContentValues(3);<br />contentValues.put(Media.DISPLAY_NAME, "WEN1885_TITLE");<br />contentValues.put(Media.DESCRIPTION, "WEN1885_DESCRIPTION");<br />getContentResolver().update(imageFileUri, contentValues, null, null);

完整的代碼例子如下,先看layout/main.xml檔案:

<?xml version="1.0" encoding="utf-8"?><br /><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"<br /> android:orientation="vertical"<br /> android:layout_width="fill_parent"<br /> android:layout_height="fill_parent"<br /> ><br /><ImageView<br />android:id="@+id/ReturnedImageView"<br /> android:layout_width="wrap_content"<br /> android:layout_height="wrap_content"/><br /><TextView<br />android:layout_width="wrap_content"<br />android:layout_height="wrap_content"<br />android:text="Title:"<br />android:id="@+id/TitleTextView" /><br /><EditText<br />android:layout_width="fill_parent"<br />android:layout_height="wrap_content"<br />android:id="@+id/TitleEditText"/><br /><TextView<br />android:layout_width="wrap_content"<br />android:layout_height="wrap_content"<br />android:text="Description"<br />android:id="@+id/DescriptionTextView"/><br /><EditText<br />android:layout_width="fill_parent"<br />android:layout_height="wrap_content"<br />android:id="@+id/DescriptionEditText"/><br /><Button<br />android:layout_width="wrap_content"<br />android:layout_height="wrap_content"<br />android:id="@+id/TakePictureButton"<br />android:text="Take Picture"/><br /><Button<br />android:layout_width="wrap_content"<br />android:layout_height="wrap_content"<br />android:id="@+id/SaveDataButton"<br />android:text="Save Data"/><br /></LinearLayout>

完整的Java代碼如下:

package hust.iprai.asce1885.promedia;</p><p>import java.io.FileNotFoundException;</p><p>import android.app.Activity;<br />import android.content.ContentValues;<br />import android.content.Intent;<br />import android.graphics.Bitmap;<br />import android.graphics.BitmapFactory;<br />import android.net.Uri;<br />import android.os.Bundle;<br />import android.provider.MediaStore.Images.Media;<br />import android.util.Log;<br />import android.view.View;<br />import android.view.View.OnClickListener;<br />import android.widget.Button;<br />import android.widget.EditText;<br />import android.widget.ImageView;<br />import android.widget.TextView;<br />import android.widget.Toast;</p><p>public class MediaStoreCameraActivity extends Activity {<br />final static int CAMERA_RESULT = 0;</p><p>Uri imageFileUri = null;</p><p>// User interface elements, specified in res/layout/main.xml<br />ImageView returnedImageView;<br />Button takePictureButton;<br />Button saveDataButton;<br />TextView titleTextView;<br />TextView descriptionTextView;<br />EditText titleEditText;<br />EditText descriptionEditText;</p><p>@Override<br />protected void onCreate(Bundle savedInstanceState) {<br />super.onCreate(savedInstanceState);</p><p>// Set the content view to be what is defined in the res/layout/main.xml file<br />setContentView(R.layout.main);</p><p>// Get references to UI elements<br />returnedImageView = (ImageView) findViewById(R.id.ReturnedImageView);<br />takePictureButton = (Button) findViewById(R.id.TakePictureButton);<br />saveDataButton = (Button) findViewById(R.id.SaveDataButton);<br />titleTextView = (TextView) findViewById(R.id.TitleTextView);<br />descriptionTextView = (TextView) findViewById(R.id.DescriptionTextView);<br />titleEditText = (EditText) findViewById(R.id.TitleEditText);<br />descriptionEditText = (EditText) findViewById(R.id.DescriptionEditText);</p><p>// Set all except takePictureButton to not be visible initially<br />// View.GONE is invisible and doesn't take up space in the layout<br />returnedImageView.setVisibility(View.GONE);<br />saveDataButton.setVisibility(View.GONE);<br />titleTextView.setVisibility(View.GONE);<br />descriptionTextView.setVisibility(View.GONE);<br />titleEditText.setVisibility(View.GONE);<br />descriptionEditText.setVisibility(View.GONE);</p><p>// When the Take Picture Button is clicked<br />takePictureButton.setOnClickListener(new OnClickListener() {</p><p>public void onClick(View v) {<br />// Add a new record without the bitmap<br />// return the URI of the new record<br />imageFileUri = getContentResolver().insert(<br />Media.EXTERNAL_CONTENT_URI, new ContentValues());<br />// Start the Camera App<br />Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);<br />it.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri);<br />startActivityForResult(it, CAMERA_RESULT);<br />}</p><p>});</p><p>saveDataButton.setOnClickListener(new OnClickListener() {</p><p>public void onClick(View v) {<br />// Update the MediaStore record with Title and Description<br />ContentValues contentValues = new ContentValues(3);<br />contentValues.put(Media.DISPLAY_NAME, titleEditText.getText().toString());<br />contentValues.put(Media.DESCRIPTION, descriptionEditText.getText().toString());<br />getContentResolver().update(imageFileUri, contentValues, null, null);</p><p>// Tell the user<br />Toast bread = Toast.makeText(MediaStoreCameraActivity.this, "Record Updated", Toast.LENGTH_LONG);<br />bread.show();</p><p>// Go back to the initial state, set Take Picture Button Visible<br />// hide other UI elements<br />takePictureButton.setVisibility(View.VISIBLE);<br />returnedImageView.setVisibility(View.GONE);<br />titleTextView.setVisibility(View.GONE);<br />descriptionTextView.setVisibility(View.GONE);<br />titleEditText.setVisibility(View.GONE);<br />descriptionEditText.setVisibility(View.GONE);<br />}</p><p>});<br />}</p><p>@Override<br />protected void onActivityResult(int requestCode, int resultCode, Intent data) {<br />super.onActivityResult(requestCode, resultCode, data);</p><p>if (RESULT_OK == resultCode) {<br />// The Camera App has returned<br />// Hide the Take Picture Button<br />takePictureButton.setVisibility(View.GONE);</p><p>// Show the other UI elements<br />saveDataButton.setVisibility(View.VISIBLE);<br />returnedImageView.setVisibility(View.VISIBLE);<br />titleTextView.setVisibility(View.VISIBLE);<br />descriptionTextView.setVisibility(View.VISIBLE);<br />titleEditText.setVisibility(View.VISIBLE);<br />descriptionEditText.setVisibility(View.VISIBLE);</p><p>// Scale the image<br />int dw = 200; // Make it at most 200 pixels wide<br />int dh = 200; // Make it at most 200 pixels tall</p><p>BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();<br />bmpFactoryOptions.inJustDecodeBounds = true;<br />Bitmap bmp = null;<br />try {<br />bmp = BitmapFactory.decodeStream(<br />getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions);<br />} catch (FileNotFoundException e) {<br />e.printStackTrace();<br />}<br />int heightRatio = (int) Math.ceil(bmpFactoryOptions.outHeight/(float)dh);<br />int widthRatio = (int) Math.ceil(bmpFactoryOptions.outWidth/(float)dw);</p><p>Log.v("HEIGHTRATIO", "" + heightRatio);<br />Log.v("WIDTHRATIO", "" + widthRatio);</p><p>// If both of the ratios are greater than 1<br />// one of the sides of the image is greater than the screen<br />if ((heightRatio > 1) && (widthRatio > 1)) {<br />if (heightRatio > widthRatio) {<br />// Height ratio is larger, scale according to it<br />bmpFactoryOptions.inSampleSize = heightRatio;<br />} else {<br />// Width ratio is larger, scale according to it<br />bmpFactoryOptions.inSampleSize = widthRatio;<br />}<br />}</p><p>// Decode it for real<br />bmpFactoryOptions.inJustDecodeBounds = false;<br />try {<br />bmp = BitmapFactory.decodeStream(<br />getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions);<br />} catch (FileNotFoundException e) {<br />e.printStackTrace();<br />Log.v("ERROR", e.toString());<br />}</p><p>// Display it<br />returnedImageView.setImageBitmap(bmp);<br />}<br />}<br />}

2)使用MediaStore來檢索映像資料

MediaStore,跟所有的content provider一樣使用類似於資料庫操作的方式來檢索資料。從指定的Uri中選擇資料記錄,之後通過Cursor對象來對結果進行迭代處理。

首先需要建立一個字串數組來表示希望返回的列類型,MediaStore中映像資料的標準列類型在MediaStore.Images.Media類中:

String[] columns =<br /> {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME};

執行實際的查詢操作使用Activity的managedQuery函數,第一個參數是URI,第二個參數是列名組成的字串數組,第三個參數是WHERE語句,後面跟的參數是WHERE包含的參數,最後一個參數是ORDER BY語句:

long oneHourAgo = System.currentTimeMillis()/1000 - (60*60);<br />String[] whereValues = {"" + oneHourAgo};<br />// 指定返回結果的列<br />String[] columns = {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME, Media.DATE_ADDED};<br />// 獲得遊標<br />Cursor cursor = managedQuery(Media.EXTERNAL_CONTENT_URI, columns, Media.DATE_ADDED + " > ?",whereValues, Media.DATE_ADDED + " ASC");<br />// 返回指定列的索引<br />int displayColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);<br />// 移到遊標的開始處<br />if (cursor.moveToFirst()) {<br />String displayName = cursor.getString(displayColumnIndex);<br />}

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

<?xml version="1.0" encoding="utf-8"?><br /><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"<br /> android:orientation="vertical"<br /> android:layout_width="fill_parent"<br /> android:layout_height="fill_parent"<br /> ><br /><ImageButton<br />android:layout_width="wrap_content"<br />android:layout_height="wrap_content"<br />android:id="@+id/ImageButton"/><br /><TextView<br />android:layout_width="fill_parent"<br />android:layout_height="wrap_content"<br />android:id="@+id/TitleTextView"<br />android:text="Image Title"/><br /></LinearLayout>

Java代碼如下:

package hust.iprai.asce1885.promedia;</p><p>import android.app.Activity;<br />import android.database.Cursor;<br />import android.graphics.Bitmap;<br />import android.graphics.BitmapFactory;<br />import android.os.Bundle;<br />import android.provider.MediaStore;<br />import android.provider.MediaStore.Images.Media;<br />import android.util.Log;<br />import android.view.View;<br />import android.view.View.OnClickListener;<br />import android.widget.ImageButton;<br />import android.widget.TextView;</p><p>public class MediaStoreGallery extends Activity {</p><p>public final static int DISPLAYWIDTH = 200;<br />public final static int DISPLAYHEIGHT = 200;</p><p>TextView titleTextView;<br />ImageButton imageButton;</p><p>Cursor cursor;<br />Bitmap bmp;<br />String imageFilePath;<br />int fileColumn;<br />int titleColumn;<br />int displayColumn;</p><p>@Override<br />protected void onCreate(Bundle savedInstanceState) {<br />super.onCreate(savedInstanceState);<br />setContentView(R.layout.main);</p><p>titleTextView = (TextView) findViewById(R.id.TitleTextView);<br />imageButton = (ImageButton) findViewById(R.id.ImageButton);</p><p>String[] columns = {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME};<br />cursor = managedQuery(Media.EXTERNAL_CONTENT_URI, columns, null, null, null);</p><p>// 注意:Media.DATA是MediaStore.Images.Media.DATA的縮寫<br />fileColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);<br />titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE);<br />displayColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME);</p><p>if (cursor.moveToFirst()) {<br />titleTextView.setText(cursor.getString(titleColumn));</p><p>imageFilePath = cursor.getString(fileColumn);<br />bmp = getBitmap(imageFilePath);</p><p>// Display it<br />imageButton.setImageBitmap(bmp);<br />}</p><p>imageButton.setOnClickListener(new OnClickListener() {</p><p>public void onClick(View v) {<br />if (cursor.moveToNext()) {<br />titleTextView.setText(cursor.getString(displayColumn));</p><p>imageFilePath = cursor.getString(fileColumn);<br />bmp = getBitmap(imageFilePath);<br />imageButton.setImageBitmap(bmp);<br />}<br />}</p><p>});<br />}</p><p>private Bitmap getBitmap(String imageFilePath) {<br />// Load up the image's dimensions not the image itself<br />BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();<br />bmpFactoryOptions.inJustDecodeBounds = true;<br />Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);</p><p>int heightRatio = (int) Math.ceil(bmpFactoryOptions.outHeight/(float)DISPLAYHEIGHT);<br />int widthRatio = (int) Math.ceil(bmpFactoryOptions.outWidth/(float)DISPLAYWIDTH);</p><p>Log.v("HEIGHTRATIO", "" + heightRatio);<br />Log.v("WIDTHRATIO", "" + widthRatio);</p><p>// If both of the ratios are greater than 1, one of the sides of<br />// the image is greater than the screen<br />if ((heightRatio > 1) && (widthRatio > 1)) {<br />if (heightRatio > widthRatio) {<br />bmpFactoryOptions.inSampleSize = heightRatio;<br />} else {<br />bmpFactoryOptions.inSampleSize = widthRatio;<br />}<br />}</p><p>// Decode it for real<br />bmpFactoryOptions.inJustDecodeBounds = false;<br />bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);</p><p>return bmp;<br />}<br />}

2)內部中繼資料

EXIF,可交換影像檔格式(Exchangeable Image File Format),是將中繼資料儲存到影像檔裡的標準格式。它的資料存放區與JPEG格式是完全相同的,它就是在JPEG格式頭部插入了數位照片的拍攝資訊。

EXIF資料中包含很多與映像拍攝緊密相關的技術參數,例如曝光時間ExposureTime和快門速度ShutterSpeedValue等。還有一些參數是我們可以在後續進行填充或修改的,例如:

UserComment: 使用者評論

ImageDescription:映像的描述

Artist:映像的建立者或者拍攝者

Copyright:著作權

Software:建立映像使用的軟體

Android提供了方便的介面ExifInterface來讀寫EXIF資料:

ExifInterface ei = new ExifInterface(imageFilePath);<br />String imageDescription = ei.getAttribute("ImageDescription");<br />if (null != imageDescription) {<br />Log.v("EXIF", imageDescription);<br />}

儲存EXIF資料到影像檔中的程式碼片段如下:

ExifInterface ei = new ExifInterface(imageFilePath);<br />ei.setAttribute("ImageDescription", "ASCE1885");

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.