Android FirebaseVisionImage Camera orientation - android

I am writing an app that requires the Firebase ml kit to read text (FirebaseVisionImage) and i am using this Google Firebase Page for instructions.
However I am having issues using the MediaStore.ACTION_IMAGE_CAPTURE Intent. When i run the app, take a photo and then try and process the image i mostly get back null from the text recogniser. I believe this is due to the orientation not being provided or that on Android the ACTION_IMAGE_CAPTURE is only using the thumbnail?
When it does (kind of) work (not return null) the text recogniser is not recognising any of the text and instead may return just a few characters. It is probably worth noting that i have developed a prototype of this same app on iOS and it is working fine. My take away from this is that it is how the image is captured and returned between the two platforms. On iOS i call a function to get the image orientation and parse that in to the FirebaseVision function.
The documentation also uses CameraX in some of its examples however i am not using this as it seems to still be in alpha stage of development.
Have i missed something? How do i get the orientation?
private fun openCamera() {
val values = ContentValues()
values.put(MediaStore.Images.Media.TITLE, R.string.app_name)
values.put(MediaStore.Images.Media.DESCRIPTION, "Capturing Photo!")
image_uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
//camera intent
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, image_uri)
startActivityForResult(cameraIntent, IMAGE_CAPTURE_CODE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == Activity.RESULT_OK) {
//process captured image
//image_uri
Log.d(TAG, "$image_uri")
val bmp = MediaStore.Images.Media.getBitmap(contentResolver, image_uri)
val image = FirebaseVisionImage.fromBitmap(bmp)
val detector = FirebaseVision.getInstance().onDeviceTextRecognizer
val result = detector.processImage(image)
.addOnSuccessListener { firebaseVisionText ->
// Task completed successfully
// ...
Log.d(TAG, "Process complete -> ${firebaseVisionText.text}")
val resultText = firebaseVisionText.text
Log.d(TAG, "$resultText")
}
.addOnFailureListener { e ->
Log.d(TAG, "Process failed $e")
}
} else {
Log.d(TAG, "onActivityResult -> resultCode: $resultCode")
}
}

Related

Deprecated startActivityForResult and onActivityResult and File Not Uploaded Successfully

There is no error and no problem with clicking the button, but the voice file does not upload to the Firestore.
Audio selection is possible, but the message "Successfully Uploaded:" is not output, and the voice file is not uploaded to the Fire Store.
I think the cancellation line in onActivityResult and startActivityForResult is the problem.
How do I get rid of the cancellation line? And is there any other reason why the file doesn't go up on the fire store?
++I modified the Firestore rules.
MainActivity.kt
import ...
class MainActivity : AppCompatActivity() {
val AUDIO : Int = 0
lateinit var uri: Uri
lateinit var mStorage: StorageReference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val musicBtn = findViewById<View>(R.id.musicBtn) as Button
mStorage = FirebaseStorage.getInstance().getReference("Uploads")
musicBtn.setOnClickListener(View.OnClickListener {
view -> val intent = Intent()
intent.setType("audio/*")
intent.setAction(Intent.ACTION_GET_CONTENT)
startActivityForResult(Intent.createChooser(intent, "Select MP3"), AUDIO)
})
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val uriTxt = findViewById<View>(R.id.uriTxt)as TextView
if (requestCode== RESULT_OK){
if (requestCode == AUDIO) {
uri = data!!.data!!
uriTxt.text = uri.toString()
upload()
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private fun upload() {
var mReference = mStorage.child(uri.lastPathSegment!!)
try {
mReference.putFile(uri).addOnSuccessListener {
taskSnapshot: UploadTask.TaskSnapshot? -> var url = taskSnapshot!!
val dwnTxt = findViewById<View>(R.id.dwnTxt) as TextView
dwnTxt.text = url.toString()
Toast.makeText(this, "Successfully Uploaded :)", Toast.LENGTH_LONG).show()
}
}catch (e: Exception) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show()
}
}
}
enter image description here
A cancellation line is created as shown in the image. How do I solve it?
startActivityResult and onActivityResult have been deprecated. You need to migrate to registerForActivityResult and ActivityResultContracts methods.
A few of the examples are given below:
https://developer.android.com/codelabs/android-app-permissions
Predefined Contracts - https://developer.android.com/reference/kotlin/androidx/activity/result/contract/ActivityResultContracts
https://developer.android.com/reference/kotlin/androidx/activity/result/contract/ActivityResultContract
https://developer.android.com/training/basics/intents/result
Another easier way you can do this is by ignoring the error and using #Deprecated("Deprecated in Java") annotation but it is recommended that you migrate.
The cancellation line means those functions are deprecated, but in this case, that is not your problem because they still behave as they used to.
This might be your issue. You have a typo here:
if (requestCode== RESULT_OK){
if (requestCode == AUDIO) {
You should be checking if the resultCode is RESULT_OK, not the requestCode. So currently your if statement will not run unless your AUDIO request code happens to be the same as Activity.RESULT_OK.
If that still doesn't solve it, you should add an OnFailureListener to find out what's happening. You can put a debug breakpoint in your OnFailureListener so you can inspect the error object for what the problem is.

How can I pass a variable control name to findViewById in Kotlin?

I am trying to learn Kotlin and I'm building a simple example as I go. I have 3 image buttons that open the camera and take a photo. The thumbnail is then set into an ImageView. I've used the examples from https://developer.android.com/training/camera/photobasics?hl=en to get the basics working (figuring if I can make it work for one, it'll work for all. It does indeed work for one, but I can't figure out how to make it one function that drops the thumbnail into the correct ImageView.
Inside my onCreate I have the listener for each of the buttons that will invoke the camera:
camRead1.setOnClickListener {dispatchTakePictureIntent() }
camRead2.setOnClickListener {dispatchTakePictureIntent() }
camRead3.setOnClickListener {dispatchTakePictureIntent() }
And I took the sample from the url above:
val REQUEST_IMAGE_CAPTURE = 1
private fun dispatchTakePictureIntent() {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
} catch (e: ActivityNotFoundException) {
// display error state to the user
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val thumb: ImageView = findViewById(R.id.thumbRead1)
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
val imageBitmap = data.extras.get("data") as Bitmap
thumb.setImageBitmap(imageBitmap)
}
}
and pasted it into my class MainActivity, and after I replaced imageView in the override function with a variable (thumb) and added the super, it worked perfectly for the first one.
However, I am trying to get 3 photos, read1, read2, and read3 which each need to display the thumb in thumbRead1, thumbRead2 and thumbRead3. I can't figure out how the onActivityResult is executed since the call inside dispatchTakePictureIntent is calling startActivityForResult (especially as Android Studio says that startActivityForResult is deprecated).
Obviously, once onActivityResult executes, I can see that thumb defines R.id.thumbRead1 and receives imageBitmap but I don't understand how I can make it aware of the button that was clicked.
Without understanding how onActivityResult is called, I'm thinking that if I can do something like:
findViewById(R.id("thumbRead" + imgID))
to define the specific ImageView that I want the photo pasted into. Am I on the right track here? If not, what is the recommended way of doing this?
Note they've recently added what's supposed to be a cleaner way of starting other activities for results and getting the results, explained here. But since you're already doing it the traditional way, I'll explain how to get that working.
I think the easiest thing to do in this situation is just make more request codes, so you can check which request it was.
val REQUEST_IMAGE_CAPTURE_SOURCE_1 = 1
val REQUEST_IMAGE_CAPTURE_SOURCE_2 = 2
val REQUEST_IMAGE_CAPTURE_SOURCE_3 = 3
//...
camRead1.setOnClickListener { dispatchTakePictureIntent(REQUEST_IMAGE_CAPTURE_SOURCE_1) }
camRead2.setOnClickListener { dispatchTakePictureIntent(REQUEST_IMAGE_CAPTURE_SOURCE_2) }
camRead3.setOnClickListener { dispatchTakePictureIntent(REQUEST_IMAGE_CAPTURE_SOURCE_3) }
//...
private fun dispatchTakePictureIntent(requestCode: Int) {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
startActivityForResult(takePictureIntent, requestCode)
} catch (e: ActivityNotFoundException) {
// display error state to the user
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode != RESULT_OK) {
// possibly show message to user
return
}
val imageViewId = when (requestCode) {
REQUEST_IMAGE_CAPTURE_SOURCE_1 -> R.id.thumbRead1
REQUEST_IMAGE_CAPTURE_SOURCE_2 -> R.id.thumbRead2
REQUEST_IMAGE_CAPTURE_SOURCE_3 -> R.id.thumbRead3
}
val imageView = findViewById<ImageView>(imageViewId)
imageView.imageBitmap = data.extras.get("data") as Bitmap
}
By the way, if you want to get an ID for a view using the String like you were showing you were trying, you would do it like this:
val viewId = resources.getIdentifier("thumbRead$imgId", "id", packageName)
val imageView = findViewById<ImageView>(viewId)
You need to pass different request code for each call and pass it to the dispatchTakePictureIntent function. You do not need to get id by findviewbyid. You simply can add the image on the basis of the request code.
val REQUEST_IMAGE_CAPTURE_ONE = 1
val REQUEST_IMAGE_CAPTURE_TWO = 2
val REQUEST_IMAGE_CAPTURE_THREE = 3
private fun dispatchTakePictureIntent(requestCode: Int) {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
startActivityForResult(takePictureIntent, requestCode)
} catch (e: ActivityNotFoundException) {
// display error state to the user
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) {
val imageBitmap = data.extras.get("data") as Bitmap
if (requestCode == REQUEST_IMAGE_CAPTURE_ONE ) {
thumbRead1.setImageBitmap(imageBitmap)
}else if (requestCode == REQUEST_IMAGE_CAPTURE_TWO ) {
thumbRead2.setImageBitmap(imageBitmap)
}else if (requestCode == REQUEST_IMAGE_CAPTURE_THREE ) {
thumbRead3.setImageBitmap(imageBitmap)
}
}
}

ExifInterface not working with files from sdcard

So I have some code to choose an image from phone gallery and display it in an ImageView and also use it's URI in ExifInterface and get the exif data.
But it seems that, only works for the images in internal storage and not for external storage like sdcard.
So here is what I got:
I have a button that when it's clicked, First it checkes to see if the app has READ_EXTERNAL_STORAGE permission and if not it asks for it.
After it's granted with the permission it launches the function below :
private fun launchIntentForPhotos() {
val gallery = Intent(Intent.ACTION_PICK)
gallery.type = "image/*"
startActivityForResult(Intent.createChooser(gallery, "Choose an image"), PICK_PHOTO_CODE)
}
and than for onActivityResult I have this:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == PICK_PHOTO_CODE) {
imageUri = data?.data
imageView.setImageResource(0)
imageView.setImageURI(imageUri)
showExif(imageUri)
}
}
and finally the function for EXIF data:
private fun showExif(imageUri: Uri?) {
val inputStream :InputStream
try
{
inputStream = imageUri?.let { contentResolver.openInputStream(it) }!!
val exifInterface = ExifInterface(inputStream)
// Now you can extract any Exif tag you want
// Assuming the image is a JPEG or supported raw format
val imgWidthExif: String? = exifInterface?.getAttribute(ExifInterface.TAG_IMAGE_WIDTH)
}
catch (e: IOException) {
// Handle any errors
Log.v(TAG, "ERROR")
Toast.makeText(this, "Some went wrong!", Toast.LENGTH_LONG).show()
}
The showExif doesn't work when selecting an image from external storage and also I get this error:
W/ImageView: resolveUri failed on bad bitmap uri: content://com.google.android.apps.photos.contentprovider/-1/1/content....
Can Someone please tell me what I'm doing wrong?!
Ok I found a solution and it works fine for me!
Instead of using ACTION_PICK I used ACTION_GET_CONTENT like this:
private fun launchIntentForPhotos() {
val gallery = Intent(Intent.ACTION_GET_CONTENT, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
gallery.type = "image/*"
startActivityForResult(Intent.createChooser(gallery, "Choose an image"), PICK_PHOTO_CODE)
}

How can I set a request code under different conditions for my activity to bypass onActivityResult?

I have a program that allows me to store data(pictures and metadata with the taken picture) during the execution of a picture being taking with the android system camera activity... but I have code in place to make sure that the user enters data into a popup activity before the camera activity is displayed by using the OnActivityResult function(this way the user's photo has information that is stored as metadata in my firebase database). I was wondering If I can set a request code that wouldn't be equal to the REQUESTCODE2 so that under the condition that my back button is pressed(which will still result in the REQUESTCODE2 being returned for the com.example.myapplication.nameofphoto activity, which then will trigger takepic()) I can purposely make sure that the request code is faulty so that takepic() does not trigger and I don't store null data into my database.
for your information: nameofpersonvar , and nameofphotovar are both in a different class and is the information from the popup activity
private const val REQUESTCODE = 2
private const val REQUESTCODE2 = 3
fun take_pic(){
val takephotoIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takephotoIntent.resolveActivity(this.packageManager) != null) {
startActivityForResult(takephotoIntent, REQUESTCODE)
} else {
Toast.makeText(this, "Unable To access Camera... ", Toast.LENGTH_LONG)
.show()
}
}
photoButton.setOnClickListener {
val action3 = Intent(this , com.example.myapplication.nameofphoto::class.java)
startActivityForResult(action3, REQUESTCODE2 )
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (REQUESTCODE == requestCode && resultCode == Activity.RESULT_OK) {
//Compressing the bitmap(image) into a byte[] to match the input of the .putbytes method
val userimage = data?.extras?.get("data") as Bitmap
val byteoutput = ByteArrayOutputStream()
userimage.compress(Bitmap.CompressFormat.JPEG,100 , byteoutput)
val data = byteoutput.toByteArray()
//ref to the firebase "bucket" database
val storageinfo = FirebaseStorage.getInstance().getReference("/Images" )
//extra data that shows who the images belong to (users)
val metadatastoreage = storageMetadata {
setCustomMetadata("Name of person" , nameofpersonvar)
setCustomMetadata("Name of photo" , nameofphotovar)}
storageinfo.putBytes(data, metadatastoreage)
}else if (requestCode ==REQUESTCODE2) {
take_pic()
}
else {
super.onActivityResult(requestCode, resultCode, data)
}
}
Then why don't you send some result code different from the back press method of the current activity opened and check if the result is successful then take pick otherwise do something.
send this as result code from back press method. RESULT_CANCELED
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (REQUESTCODE == requestCode && resultCode == Activity.RESULT_OK) {
//Compressing the bitmap(image) into a byte[] to match the input of the .putbytes method
val userimage = data?.extras?.get("data") as Bitmap
val byteoutput = ByteArrayOutputStream()
userimage.compress(Bitmap.CompressFormat.JPEG,100 , byteoutput)
val data = byteoutput.toByteArray()
//ref to the firebase "bucket" database
val storageinfo = FirebaseStorage.getInstance().getReference("/Images" )
//extra data that shows who the images belong to (users)
val metadatastoreage = storageMetadata {
setCustomMetadata("Name of person" , nameofpersonvar)
setCustomMetadata("Name of photo" , nameofphotovar)}
storageinfo.putBytes(data, metadatastoreage)
return
}
if (requestCode ==REQUESTCODE2 && resultcode == Activity.RESULT_OK) {
take_pic()
} else {
//back pressed do something.
//finish etc
}
}
Edit: You can override the onBackPressed() in the popup activity and send some data using intent to the parent activity. for ex.
Intent resultIntent = new Intent();
// TODO Add extras or a data URI to this intent as appropriate.
resultIntent.putExtra("user_pic_click", "some data");
setResult(Activity.RESULT_OK, resultIntent);
finish();

Read barcode info from bitmap using ML kit

I want to get some info from barcode using my camera.
It works when I use png image downloaded from site, but when I try to get it work with a photo I took, it outputs me the empty array. Seems like I need to make some preps with the image in order to make it work.
Here is my code:
fun getTheBarCode(bitmap: Bitmap) {
val options = FirebaseVisionBarcodeDetectorOptions.Builder()
.setBarcodeFormats(
FirebaseVisionBarcode.FORMAT_AZTEC)
.build()
val detector = FirebaseVision.getInstance().getVisionBarcodeDetector(options)
val bm = BitmapFactory.decodeResource(getResources(), R.drawable.barcode) //this is the place where I can load my downloaded barcode to make everything work!
val newBitmap = Bitmap.createScaledBitmap(bitmap, 300, 500, false)
val image = FirebaseVisionImage.fromBitmap(newBitmap)
photoImage.setImageBitmap(newBitmap)
detector.detectInImage(image)
.addOnSuccessListener {
Log.d("Success", "Success")
//empty array here, when I take picture.
}
.addOnFailureListener {
Log.d("Failed", it.message)
}
}
This is how I get the image from the camera
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CAMERA_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
val photo = data.extras.get("data") as Bitmap
getTheBarCode(photo)
}
}
Edit:
I've take a picture with my phone, scale it down to 1500x1000px and put it inside my app directory, then loaded it as a bitmap.
Still not working.
The approach you're using will only give you back thumbnail of photo (as per https://developer.android.com/training/camera/photobasics) ...that may not be sufficient for what you're trying to do. That link also contains info on how to get access to full size photo.

Categories

Resources