![Hur man extraherar text från bilder med Googles maskininlärnings-SDK - Appar Hur man extraherar text från bilder med Googles maskininlärnings-SDK - Appar](https://a.23rdpta.org/apps/how-to-extract-text-from-images-with-googles-machine-learning-sdk-2.jpg)
Innehåll
- På enheten eller i molnet?
- Skapa en app för textigenkänning med ML Kit
- Ladda ner Googles förutbildade modeller för maskininlärning
- Bygga layouten
- Handlingsfältikoner: Starta Galleri-appen
- Hantering av behörighetsbegäranden och klickhändelser
- Ändra storlek på bilder med createTempFile
- Ställ in bilden till en ImageView
- Lär en app att känna igen text
- Testa projektet
- Avslutar
Du kan också använda API: n för textigenkänning som bas för översättningsappar, eller tillgänglighetstjänster där användaren kan rikta sin kamera mot vilken text de kämpar med och få den läst högt upp för dem.
I denna handledning lägger vi grunden för ett brett utbud av innovativa funktioner genom att skapa en app som kan extrahera text från valfri bild i användarens galleri. Även om vi inte täcker det i den här självstudien, kan du också fånga text från användarens omgivning i realtid genom att ansluta den här applikationen till enhetens kamera.
På enheten eller i molnet?
Vissa av ML Kit API: er är bara tillgängliga på enheten, men några är tillgängliga på enheten och i molnet, inklusive API för textigenkänning.
Det molnbaserade Text API kan identifiera ett bredare spektrum av språk och tecken och lovar större noggrannhet än dess motsvarighet på enheten. Men det gör kräver en aktiv Internetanslutning och är endast tillgänglig för projekt på Blaze-nivå.
I den här artikeln kommer vi att köra API för textigenkänning lokalt, så att du kan följa med oavsett om du har uppgraderat till Blaze, eller om du har en gratis Firebase Spark-plan.
Skapa en app för textigenkänning med ML Kit
Skapa en applikation med inställningarna du väljer, men välj mall "Tom aktivitet" när du uppmanas.
ML Kit SDK är en del av Firebase, så du måste ansluta ditt projekt till Firebase med hjälp av sitt SHA-1-signeringscertifikat. Så här får du projektets SHA-1:
- Välj fliken "Gradle" i Android Studio.
- I panelen "Gradle projects" dubbelklickar du för att utöka projektets "root" och välj sedan "Tasks> Android> Signing Report."
- Panelen längst ner i fönstret Android Studio bör uppdateras för att visa lite information om detta projekt - inklusive dess SHA-1-signeringscertifikat.
Så här ansluter du ditt projekt till Firebase:
- Starta Firebase Console i din webbläsare.
- Välj "Lägg till projekt."
- Ge ditt projekt ett namn; Jag använder "ML Test."
- Läs villkoren och om du gärna fortsätter väljer du "Jag accepterar ..." följt av "Skapa projekt."
- Välj "Lägg till eldstad i din Android-app."
- Ange projektets paketnamn, som du hittar högst upp i MainActivity-filen och inuti manifestet.
- Ange ditt projekts SHA-1-signeringscertifikat.
- Klicka på "Registrera app."
- Välj ”Ladda ner google-services.json.” Den här filen innehåller alla nödvändiga Firebase-metadata för ditt projekt, inklusive API-nyckeln.
- Dra och släpp google-services.json-filen i Android Studio i projektets "app" -katalog.
- Öppna din build.gradle-fil på projektnivå och lägg till Google Services-klassvägen:
classpath com.google.gms: google-services: 4.0.1
- Öppna din build.gradle-fil på appnivå och lägg till beroenden för Firebase Core, Firebase ML Vision och modelltolkaren, plus Googles serviceplugin:
applicera plugin: com.google.gms.google-services ... ... ... beroenden {implementeringsfilTree (dir: libs, inkluderar:) implementering com.google.firebase: firebase-core: 16.0.1 implementering com. google.firebase: firebase-ml-vision: 16.0.0 implementering com.google.firebase: firebase-ml-model-tolk: 16.0.0
Just nu måste du köra ditt projekt så att det kan ansluta till Firebase-servrarna:
- Installera din app på antingen en fysisk Android-smartphone eller surfplatta eller en Android Virtual Device (AVD).
- I Firebase Console väljer du "Kör app för att verifiera installationen."
- Efter några ögonblick bör du se "Grattis"; välj "Fortsätt till konsolen."
Ladda ner Googles förutbildade modeller för maskininlärning
Som standard hämtar ML Kit endast modeller efter behov, så vår app kommer att ladda ner OCR-modellen när användaren försöker extrahera text för första gången.
Detta kan potentiellt ha en negativ inverkan på användarupplevelsen - tänk dig att försöka komma åt en funktion, bara för att upptäcka att appen måste ladda ner fler resurser innan den faktiskt kan leverera den här funktionen. I värsta fall kanske din app inte ens kan ladda ner de resurser den behöver, när den behöver dem, till exempel om enheten inte har någon internetanslutning.
För att se till att detta inte händer med vår app kommer jag att ladda ner den nödvändiga OCR-modellen vid installationstiden, vilket kräver några ändringar i Maniest.
Medan vi har manifestet öppet kommer jag också att lägga till WRITE_EXTERNAL_STORAGE-behörigheten, som vi kommer att använda det senare i den här handboken.
Bygga layouten
Låt oss få de enkla sakerna ur vägen och skapa en layout som består av:
- En ImageView. Till en början visar detta en platshållare, men den kommer att uppdateras när användaren väljer en bild från sitt galleri.
- En knapp, som utlöser textuttag.
- En TextView, där vi visar den extraherade texten.
- En ScrollView. Eftersom det inte finns någon garanti för att den extraherade texten kommer att passa snyggt på skärmen kommer jag att placera TextView i en ScrollView.
Här är den färdiga filen Activity_main.xml:
Den här layouten hänvisar till ett "ic_placeholder" -dragbart, så låt oss skapa det nu:
- Välj "File> New> Image Asset" i verktygsfältet Android Studio.
- Öppna rullgardinsmenyn "Ikontyp" och välj "Åtgärdsfält och tabbikoner."
- Se till att alternativknappen "Clip Art" är vald.
- Klicka på knappen "Clip Art".
- Välj den bild du vill använda som platshållare; Jag använder "Lägg till foton."
- Klicka på "OK."
- Öppna rullgardinsmenyn "Tema" och välj "HOLO_LIGHT."
- I fältet "Namn" anger du "ic_placeholder."
- Klicka på "Nästa." Läs informationen, och om du gärna fortsätter klickar du på "Slutför."
Handlingsfältikoner: Starta Galleri-appen
Därefter kommer jag att skapa en åtgärdsfält som kommer att starta användarens galleri, redo för dem att välja en bild.
Du definierar åtgärdsfältikoner i en menyresursfil som lever i katalogen "res / menu". Om ditt projekt inte innehåller den här katalogen, måste du skapa den:
- Kontrollera-klicka på projektets "res" -katalog och välj "New> Android Resource Directory."
- Öppna rullgardinsmenyn "Resurstyp" och välj "meny."
- "Katalognamn" bör uppdateras till "meny" automatiskt, men om det inte gör det måste du byta namn på det manuellt.
- Klicka på "OK."
Du är nu redo att skapa menyresursfilen:
- Kontrollklicka på projektets "meny" -katalog och välj "Ny> Menyresursfil."
- Namnge den här filen "my_menu."
- Klicka på "OK."
- Öppna filen "my_menu.xml" och lägg till följande:
Menyfilen refererar till en "action_gallery" -sträng, så öppna projektets res / Values / Strings.xml-fil och skapa den här resursen. Medan jag är här definierar jag också de andra strängarna som vi kommer att använda i hela detta projekt.
Därefter använder du Image Asset Studio för att skapa åtgärdsfältets ikon "ic_gallery":
- Välj "File> New> Image Asset."
- Ställ in rullgardinsmenyn "Ikontyp" till "Åtgärdsfält och tabbikoner."
- Klicka på knappen "Clip Art".
- Välj en dragbar; Jag använder "image."
- Klicka på "OK."
- För att se till att denna ikon är tydligt synlig i åtgärdsfältet, öppna rullgardinsmenyn "Tema" och välj "HOLO_DARK."
- Namnge denna ikon "ic_gallery."
- "Klicka på" Nästa "följt av" Finish. "
Hantering av behörighetsbegäranden och klickhändelser
Jag kommer att utföra alla uppgifter som inte är direkt relaterade till API: n för textigenkänning i en separat ClassActivity-klass, inklusive inställning av menyn, hantering av åtgärdsfältets klickhändelser och begär åtkomst till enhetens lagring.
- Välj "File> New> Java class" i Android Studio: s verktygsfält.
- Namnge denna klass "BaseActivity."
- Klicka på "OK."
- Öppna BaseActivity och lägg till följande:
import android.app.Aktivitet; import android.support.v4.app.ActivityCompat; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; importera android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.content.DialogInterface; import android.content.Intent; import android.Manifest; import android.provider.MediaStore; import android.view.Menu; import android.view.MenuItem; import android.content.pm.PackageManager; import android.net.Uri; import android.provider.Settings; importera android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.io.File; public class BaseActivity utökar AppCompatActivity {public static final int WRITE_STORAGE = 100; public static final int SELECT_PHOTO = 102; public static final String ACTION_BAR_TITLE = "action_bar_title"; offentligt filfoto; @Override skyddat tomrum onCreate (@Nullable Bundle SavedInstanceState) {super.onCreate (SavedInstanceState); ActionBar actionBar = getSupportActionBar (); if (actionBar! = null) {actionBar.setDisplayHomeAsUpEnabled (true); actionBar.setTitle (getIntent () getStringExtra (ACTION_BAR_TITLE).); }} @Override public boolean onCreateOptionsMenu (Meny meny) {getMenuInflater (). Inflate (R.menu.my_menu, menu); tillbaka sant; } @Override public boolean onOptionsItemSelected (MenuItem item) {switch (item.getItemId ()) {// Om “gallery_action” är valt, så ... // case R.id.gallery_action: //...check vi har WRITE_STORAGE-behörigheten // checkPermission (WRITE_STORAGE); ha sönder; } return super.onOptionsItemSelected (artikel); } @Override public void onRequestPermissionsResult (int requestCode, @NonNull String permissions, @NonNull int grantResults) {super.onRequestPermissionsResult (requestCode, permissions, grantResults); switch (requestCode) {case WRITE_STORAGE: // Om behörighetsbegäran beviljas, ... ... if (grantResults.length> 0 && grantResults == PackageManager.PERMISSION_GRANTED) {//...call selectPicture // selectPicture ( ); // Om tillståndsbegäran avvisas, ... ...} annars {//...display strängen "Permission_request" // requestPermission (detta, requestCode, R.string.permission_request); } ha sönder; }} // Visa dialogrutan om tillståndsbegäran // public static void requestPermission (slutlig aktivitetsaktivitet, final int requestCode, int msg) {AlertDialog.Builder alert = new AlertDialog.Builder (aktivitet); alert.set (msg); alert.setPositiveButton (android.R.string.ok, new DialogInterface.OnClickListener () {@Override public void onClick (DialogInterface dialogInterface, int i) {dialogInterface.dismiss (); Intent permissonIntent = new Intent (Settings.ACTION_APPLILSATIONSETT) .setData (Uri.parse ("paket:" + Activity.getPackageName ())); Activity.startActivityForResult (permissonIntent, requestCode);}}); alert.setNegativeButton (android.R.string.cancel, ny DialogInterface.OnClickListener () {@Override public void onClick (DialogInterface dialogInterface, int i) {dialogInterface.dismiss ();}}); alert.setCancelable (false); alert.show (); } // Kontrollera om användaren har gett WRITE_STORAGE tillstånd // public void checkPermission (int requestCode) {switch (requestCode) {case WRITE_STORAGE: int hasWriteExternalStoragePermission = ActivityCompat.checkSelfPermission (detta, Manifest.permission.WRITE_EXTERNAL_STORAGE); // Om vi har tillgång till extern lagring ... // if (hasWriteExternalStoragePermission == PackageManager.PERMISSION_GRANTED) {//...anrop selectPicture, som startar en aktivitet där användaren kan välja en bild // selectPicture (); // Om tillstånd inte har beviljats, ... ...} annars {//...fråga om tillstånd // ActivityCompat.requestPermissions (detta, ny String {Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode); } ha sönder; }} privat tomrum selectPicture () {photo = MyHelper.createTempFile (foto); Intent intention = new Intent (Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); // Starta en aktivitet där användaren kan välja en bild // startActivityForResult (avsikt, SELECT_PHOTO); }}
Just nu bör ditt projekt klaga på att det inte kan lösa MyHelper.createTempFile. Låt oss implementera detta nu!
Ändra storlek på bilder med createTempFile
Skapa en ny klass "MyHelper". I den här klassen kommer vi att ändra storleken på användarens valda bild, redo att behandlas av API: n för textigenkänning.
import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.content.Context; import android.database. Markör; import android.os. Miljö; import android.widget.ImageView; import android.provider.MediaStore; import android.net.Uri; importera statisk android.graphics.BitmapFactory.decodeFile; importera statisk android.graphics.BitmapFactory.decodeStream; import java.io.File; importera java.io.FileNotFoundException; import java.io.FileOutputStream; importera java.io.IOException; public class MyHelper {public static String getPath (Context context, Uri uri) {String path = ""; Strängprojektion = {MediaStore.Images.Media.DATA}; Markörmarkören = context.getContentResolver () .fråga (uri, projektion, null, null, null); int column_index; if (markör! = null) {column_index = cursor.getColumnIndexOrThrow (MediaStore.Images.Media.DATA); cursor.moveToFirst (); sökväg = cursor.getString (column_index); cursor.close (); } returväg; } public static File createTempFile (File file) {File directory = new File (Environment.getExternalStorageDirectory (). getPath () + "/com.jessicathornsby.myapplication"); if (! directory.exists () ||! directory.isDirectory ()) {directory.mkdirs (); } if (fil == null) {fil = ny fil (katalog, "orig.jpg"); } returfil; } offentlig statisk Bitmap resizePhoto (File imageFile, Context context, Uri uri, ImageView-vy) {BitmapFactory.Options newOptions = new BitmapFactory.Options (); prova {decodeStream (context.getContentResolver (). openInputStream (uri), null, newOptions); int photoHeight = newOptions.outHeight; int photoWidth = newOptions.outWidth; newOptions.inSampleSize = Math.min (photoWidth / view.getWidth (), photoHeight / view.getHeight ()); return compressPhoto (imageFile, BitmapFactory.decodeStream (context.getContentResolver (). openInputStream (uri), null, newOptions)); } catch (FileNotFoundException undantag) {exception.printStackTrace (); return null; }} offentlig statisk Bitmap resizePhoto (File imageFile, String path, ImageView view) {BitmapFactory.Options-alternativ = new BitmapFactory.Options (); decodeFile (sökväg, alternativ); int photoHeight = optioner.outHeight; int photoWidth = options.outWidth; options.inSampleSize = Math.min (photoWidth / view.getWidth (), photoHeight / view.getHeight ()); return compressPhoto (imageFile, BitmapFactory.decodeFile (sökväg, alternativ)); } privat statisk Bitmap compressPhoto (File photoFile, Bitmap bitmap) {prov {FileOutputStream fOutput = new FileOutputStream (photoFile); bitmap.compress (Bitmap.CompressFormat.JPEG, 70, fOutput); fOutput.close (); } catch (IOException exception) {exception.printStackTrace (); } returnera bitmapp; }}
Ställ in bilden till en ImageView
Därefter måste vi implementera onActivityResult () i vår MainActivity-klass och ställa in användarens valda bild till vår ImageView.
import android.graphics.Bitmap; import android.os.Bundle; import android.widget.ImageView; import android.content.Intent; import android.widget.TextView; import android.net.Uri; public class MainActivity utökar BaseActivity {privat Bitmap myBitmap; privat ImageView myImageView; privat TextView myTextView; @Override skyddat tomrum onCreate (Bundle SavedInstanceState) {super.onCreate (SavedInstanceState); setContentView (R.layout.activity_main); myTextView = findViewById (R.id.textView); myImageView = findViewById (R.id.imageView); } @Override skyddat tomrum påActivityResult (int requestCode, int resultCode, Intent data) {super.onActivityResult (requestCode, resultCode, data); if (resultCode == RESULT_OK) {switch (requestCode) {case WRITE_STORAGE: checkPermission (requestCode); ha sönder; fall SELECT_PHOTO: Uri dataUri = data.getData (); Strängväg = MyHelper.getPath (detta, dataUri); if (sökväg == null) {myBitmap = MyHelper.resizePhoto (foto, detta, dataUri, myImageView); } annat {myBitmap = MyHelper.resizePhoto (foto, sökväg, myImageView); } if (myBitmap! = null) {myTextView.setText (null); myImageView.setImageBitmap (myBitmap); } ha sönder; }}}}
Kör detta projekt på en fysisk Android-enhet eller AVD och ge åtgärdsfältikonen ett klick. När du blir ombedd, ge WRITE_STORAGE behörighet och välj en bild från galleriet. den här bilden ska nu visas i appens användargränssnitt.
Nu har vi lagt grunden, vi är redo att börja extrahera lite text!
Lär en app att känna igen text
Jag vill utlösa textigenkänning som svar på en klickhändelse, så vi måste implementera en OnClickListener:
import android.graphics.Bitmap; import android.os.Bundle; import android.widget.ImageView; import android.content.Intent; import android.widget.TextView; import android.view.View; import android.net.Uri; public class MainActivity utökar BaseActivity implementerar View.OnClickListener {privat Bitmap myBitmap; privat ImageView myImageView; privat TextView myTextView; @Override skyddat tomrum onCreate (Bundle SavedInstanceState) {super.onCreate (SavedInstanceState); setContentView (R.layout.activity_main); myTextView = findViewById (R.id.textView); myImageView = findViewById (R.id.imageView); findViewById (R.id.checkText) .setOnClickListener (detta); } @Override public void onClick (View view) {switch (view.getId ()) {case R.id.checkText: if (myBitmap! = Null) {// Vi kommer att implementera runTextRecog i nästa steg // runTextRecog (); } ha sönder; }}
ML Kit kan bara bearbeta bilder när de är i FirebaseVisionImage-formatet, så vi måste konvertera vår bild till ett FirebaseVisionImage-objekt. Du kan skapa en FirebaseVisionImage från en bitmapp, media.Image, ByteBuffer eller en byte-grupp. Eftersom vi arbetar med Bitmaps måste vi anropa verktygsmetoden fromBitmap () i klassen FirebaseVisionImage och skicka den till vår Bitmap.
private void runTextRecog () {FirebaseVisionImage image = FirebaseVisionImage.fromBitmap (myBitmap);
ML Kit har olika detektorklasser för var och en av sina bildigenkänningsoperationer. För text måste vi använda klassen FirebaseVisionTextDetector, som utför optisk teckenigenkänning (OCR) på en bild.
Vi skapar en instans av FirebaseVisionTextDetector med hjälp av getVisionTextDetector:
FirebaseVisionTextDetector detektor = FirebaseVision.getInstance (). GetVisionTextDetector ();
Därefter måste vi kontrollera FirebaseVisionImage efter text genom att kalla metoden detectInImage () och skicka det till FirebaseVisionImage-objektet. Vi måste också implementera onSuccess och onFailure återuppringningar, plus motsvarande lyssnare så att vår app blir meddelad när resultaten blir tillgängliga.
detector.detectInImage (image) .addOnSuccessListener (ny OnSuccessListener Om den här operationen misslyckas kommer jag att visa en rostat bröd, men om operationen är en framgång kommer jag att ringa processExtractedText med svaret. Just nu ser min textdetekteringskod ut så här: // Skapa en FirebaseVisionImage // private void runTextRecog () {FirebaseVisionImage image = FirebaseVisionImage.fromBitmap (myBitmap); // Skapa en instans av FirebaseVisionCloudTextDetector // FirebaseVisionTextDetector detector = FirebaseVision.getInstance (). GetVisionTextDetector (); // Registrera en OnSuccessListener // detector.detectInImage (bild) .addOnSuccessListener (ny OnSuccessListener När vår app får ett onSuccess-meddelande måste vi analysera resultaten. Ett FirebaseVisionText-objekt kan innehålla element, linjer och block, där varje block typiskt motsvarar ett enda stycke text. Om FirebaseVisionText returnerar 0 block kommer vi att visa strängen "no_text", men om den innehåller ett eller flera block så kommer vi att visa den hämtade texten som en del av vår TextView. privat ogiltig processExtractedText (FirebaseVisionText firebaseVisionText) {myTextView.setText (null); if (firebaseVisionText.getBlocks (). storlek () == 0) {myTextView.setText (R.string.no_text); lämna tillbaka; } för (FirebaseVisionText.Block-block: firebaseVisionText.getBlocks ()) {myTextView.append (block.getText ()); }}} Här är den ifyllda MainActivity-koden: import android.graphics.Bitmap; import android.os.Bundle; import android.widget.ImageView; import android.content.Intent; import android.widget.TextView; import android.widget.Toast; import android.view.View; import android.net.Uri; importera android.support.annotation.NonNull; importera com.google.firebase.ml.vision.common.FirebaseVisionImage; import com.google.firebase.ml.vision.text.FirebaseVisionText; import com.google.firebase.ml.vision.text.FirebaseVisionTextDetector; import com.google.firebase.ml.vision.FirebaseVision; importera com.google.android.gms.tasks.OnSuccessListener; importera com.google.android.gms.tasks.OnFailureListener; public class MainActivity utökar BaseActivity implementerar View.OnClickListener {privat Bitmap myBitmap; privat ImageView myImageView; privat TextView myTextView; @Override skyddat tomrum onCreate (Bundle SavedInstanceState) {super.onCreate (SavedInstanceState); setContentView (R.layout.activity_main); myTextView = findViewById (R.id.textView); myImageView = findViewById (R.id.imageView); findViewById (R.id.checkText) .setOnClickListener (detta); } @Override public void onClick (View view) {switch (view.getId ()) {case R.id.checkText: if (myBitmap! = Null) {runTextRecog (); } ha sönder; }} @Override skyddat tomrum onActivityResult (int requestCode, int resultCode, Intent data) {super.onActivityResult (requestCode, resultCode, data); if (resultCode == RESULT_OK) {switch (requestCode) {case WRITE_STORAGE: checkPermission (requestCode); ha sönder; fall SELECT_PHOTO: Uri dataUri = data.getData (); Strängväg = MyHelper.getPath (detta, dataUri); if (sökväg == null) {myBitmap = MyHelper.resizePhoto (foto, detta, dataUri, myImageView); } annat {myBitmap = MyHelper.resizePhoto (foto, sökväg, myImageView); } if (myBitmap! = null) {myTextView.setText (null); myImageView.setImageBitmap (myBitmap); } ha sönder; }}} privat tomrum runTextRecog () {FirebaseVisionImage image = FirebaseVisionImage.fromBitmap (myBitmap); FirebaseVisionTextDetector detektor = FirebaseVision.getInstance (). GetVisionTextDetector (); detector.detectInImage (image) .addOnSuccessListener (ny OnSuccessListener Nu är det dags att se ML Kit's textigenkänning i aktion! Installera detta projekt på en Android-enhet eller AVD, välj en bild från galleriet och ge sedan knappen "Kontrollera texten" en knapp. Appen ska svara genom att extrahera all text från bilden och sedan visa den i en TextView. Observera att beroende på storleken på din bild och mängden text den innehåller, kan du behöva bläddra för att se all extraherad text. Du kan också ladda ner det slutförda projektet från GitHub. Nu vet du hur du kan upptäcka och extrahera text från en bild med ML Kit. Textigenkännings API är bara en del av ML Kit. Denna SDK erbjuder också streckkodsscanning, ansiktsdetektering, bildmärkning och landmärkeigenkänning, med planer på att lägga till fler API: er för vanliga mobilanvändningsfall, inklusive Smart Answer och ett ansiktskontur-API med hög densitet. Vilket ML Kit API är du mest intresserad att prova? Låt oss veta i kommentarerna nedan!Testa projektet
Avslutar