How to access camera frames in flutter quickly - camera

I would like to implement near real-time OCR on the camera feed of my flutter app. To do this I would like to access the camera data in a speedy manner.
As far as I can tell I have two options, and have hit roadblocks with both:
Take a screenshot of the CameraPreview by putting a RepaintBoundary around it and creating a RenderRepaintBoundary, and calling boundary.toImage(). The problem with this method is that the .toImage method only seems to capture the painted widgets in the boundary and not the data from the camera preview. Simmilar to the issue described here: https://github.com/flutter/flutter/issues/17687
Capture an image with controller.takePicture(filePath) from Camera 0.2.1, similar to the example docs. The problem here is that it takes super long before the image becomes available (2-3 seconds). I guess that this is because the file is saved to the disc on capture and then needs to be read from the file again.
Is there any way that one can directly access the picture information after capture, to do things like pre-process and OCR?

For "near real-time OCR", you need CameraController#startImageStream
example code
import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: _MyHomePage()));
class _MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<_MyHomePage> {
dynamic _scanResults;
CameraController _camera;
bool _isDetecting = false;
CameraLensDirection _direction = CameraLensDirection.back;
#override
void initState() {
super.initState();
_initializeCamera();
}
Future<CameraDescription> _getCamera(CameraLensDirection dir) async {
return await availableCameras().then(
(List<CameraDescription> cameras) => cameras.firstWhere(
(CameraDescription camera) => camera.lensDirection == dir,
),
);
}
void _initializeCamera() async {
_camera = CameraController(
await _getCamera(_direction),
defaultTargetPlatform == TargetPlatform.iOS
? ResolutionPreset.low
: ResolutionPreset.medium,
);
await _camera.initialize();
_camera.startImageStream((CameraImage image) {
if (_isDetecting) return;
_isDetecting = true;
try {
// await doSomethingWith(image)
} catch (e) {
// await handleExepction(e)
} finally {
_isDetecting = false;
}
});
}
Widget build(BuildContext context) {
return null;
}
}
This functionality was merged to https://github.com/flutter/plugins but it was not well documented.
Ref:
https://github.com/flutter/flutter/issues/26348
https://github.com/flutter/plugins/pull/965
https://github.com/bparrishMines/mlkit_demo/blob/master/lib/main.dart#L43
https://youtu.be/OAEWySye0BQ?t=1460

A better solution today (2022) for real-time OCR is to use the camera in a loop with a frequency of 500ms and process the image using google ML Kit's Text recognition.

Related

PagerAdapter always getting called two times in ViewPager

I am trying to make a slider between TouchImageView and PlayerView (Exoplayer) but I am unable to catch up with certain issues that are persisting even after several changes. All the suggestions and answers are welcome. Pardon my questioning skills and please let me know if more inputs are needed for your analysis. Kindly also let me know if there is any other alternative to successfully meet my expectations of properly implementing views smoothly in ViewPager.
Problem description:-
Issues related to click on view :-
When the image is clicked, the audio of next video (if any) starts playing in background.
The same issue is with PlayerView. When the video thumbnail is clicked, the audio of clicked video as well as next video plays together.
Issues related to slider :-
When an we slide and reach to an image preceding to a video, the audio starts playing in background. However, after sliding once toward video and sliding again in forward or backward direction from video for once, the audio stops. But this issue persists after viewing more than one images in forward or backward direction of video.
Attempts made by me to solve this issue :-
I tried to use playerView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {...}) method in PagerAdapter to handle player states while sliding between views. Unfortunately, I was unable to grasp to use different player states.
I also tried to use viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {...} method in StatusViewer class.
StatusViewer Java class (Setting PagerAdapter class object inViewPager) :-
modelFeedArrayList = (ArrayList<File>) getIntent().getSerializableExtra("modelFeedArrayList");
position = intent.getIntExtra("position", 0);
ImageSlideAdapter imageSlideAdapter = new ImageSlideAdapter(this,modelFeedArrayList,position);
viewPager.setAdapter(imageSlideAdapter);
viewPager.setCurrentItem(position);
viewPager.setOffscreenPageLimit(0);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
File currentFile = modelFeedArrayList.get(position);
String filePath = currentFile.toString();
if (filePath.endsWith(".jpg") || currentPage == position){
currentPage = position;
ImageSlideAdapter.player.pause();
}
else {
currentPage = position;
ImageSlideAdapter.player.play();
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
ImageSliderAdapter (PagerAdapter) (code mentioned below is inside instantiateItem):-
File currentFile = modelFeedArrayList.get(position);
String filePath = currentFile.toString();
if (currentFile.getAbsolutePath().endsWith(".mp4")) {
statusImageView.setVisibility(View.GONE);
playerView.setVisibility(View.VISIBLE);
player = new ExoPlayer.Builder(context).build();
MediaItem mediaItem = MediaItem.fromUri(filePath);
player.addMediaItem(mediaItem);
playerView.setPlayer(player);
player.prepare();
playerView.setBackgroundColor(context.getResources().getColor(android.R.color.black));
playerView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
#Override
public void onViewAttachedToWindow(View v) {
Log.d("Filepath", filePath);
Log.d("Position", "" + position);
}
#Override
public void onViewDetachedFromWindow(View v) {
if (filePath.endsWith(".jpg") || currentPage == position || modelFeedArrayList.get(currentPage).getAbsolutePath().endsWith(".jpg")){
currentPage = position;
player.pause();
Objects.requireNonNull(playerView.getPlayer()).pause();
}
else {
player.release();
Objects.requireNonNull(playerView.getPlayer()).release();
}
}
});
} else {
playerView.setVisibility(View.GONE);
statusImageView.setVisibility(View.VISIBLE);
Glide.with(context).load(modelFeedArrayList.get(position)).into(statusImageView);
statusImageView.setBackgroundColor(context.getResources().getColor(android.R.color.black));
}
Objects.requireNonNull(container).addView(itemView);
return itemView;
}
#Override
public void destroyItem(#NonNull #NotNull ViewGroup container, int position, #NonNull #NotNull Object object) {
container.removeView((ConstraintLayout) object);
}
Thank you StackOverflow community for viewing this question. I resolved the above issue by below mentioned modifications :-
Changes in ImageSliderAdapter (PagerAdapter) :-
-> Below mentioned code was added in onViewAttachedToWindow(View v) :-
if (filePath.endsWith(".jpg") || currentPage == position || modelFeedArrayList.get(currentPage).getAbsolutePath().endsWith(".jpg")){
currentPage = position;
player.pause();
Objects.requireNonNull(playerView.getPlayer()).pause();
}
else {
player.pause();
Objects.requireNonNull(playerView.getPlayer()).pause();
if (filePath.endsWith(".mp4")){
player.pause();
Objects.requireNonNull(playerView.getPlayer()).pause();
}
else {
player.play();
Objects.requireNonNull(playerView.getPlayer()).play();
}
}
-> Below mentioned code was added in onViewDetachedFromWindow(View v) :-
if (filePath.endsWith(".mp4")){
player.release();
Objects.requireNonNull(playerView.getPlayer()).release();
}
-> player.play() was added after player.prepare().
Changes in StatusViewer Java class :-
-> The below changes cured the issue of player malfunctioning and player's play state and release state. I used the smoothScroll: false in setCurrentItem.
viewPager.setCurrentItem(position,false);

How to pass data from Model instance (which has a values from api) and give it to Text() widget

How to pass data from Model instance (which has a values from api) and give it to Text() widget?
But without an examples with listview please. I need to just give the values to Text widget.
In the first block, my weather instance has a values, but I don't know how to give this values to my Text widget. I'll be grateful if you help me
home.dart file:
import 'package:flutter/material.dart';
import 'package:weather_mix_app/models/weather_model.dart';
import 'package:weather_mix_app/services/weather.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<WeatherModel> weather = new List<WeatherModel>();
#override
void initState() {
super.initState();
setupWeather();
}
setupWeather() async {
Weather weatherInstance = Weather();
await weatherInstance.fetchData();
weather = weatherInstance.weather;
}
#override
Widget build(BuildContext context) {
return Container(child: Text('WHAT I NEED TO WRITE...'));
}
}
My weather.dart (fetching data from api)
import 'dart:convert';
import 'package:http/http.dart';
import 'package:weather_mix_app/models/weather_model.dart';
class Weather {
List<WeatherModel> weather = [];
Future<void> fetchData() async {
try {
Response response = await get(
'http://api.weatherstack.com/current?access_key=3327f40525a4577b7d0a75aea0c7d44f&query=New%20York');
Map jsonData = jsonDecode(response.body);
WeatherModel weatherModel = WeatherModel(
description: jsonData['current']['weather_descriptions'],
temp: jsonData['current']['temperature'],
feels_like: jsonData['current']['feelslike'],
query: jsonData['request']['query']);
weather.add(weatherModel);
print(weather);
} catch (e) {
print('caught error! $e');
}
}
}
and my weather_model.dart
class WeatherModel {
dynamic description;
dynamic temp;
dynamic feels_like;
dynamic query;
WeatherModel({this.description, this.temp, this.feels_like, this.query});
}
you can access the List<WeatherModel> weather = []; from Weather in your home.dart, by doing Weather().weather , since it is a list, if you only want to show the first data you can do it in the Text(Weather().weather[0].temp)

Can I use streamBuilder in fetching live data from an Api

Good day guys. I'm making a football score live data app in flutter. I don't know how to get around this, if I use http.get, I'll have to refresh Everytime to get the recent data. I don't know if a streamBuilder would work and how to go about it. Thanks in advance for your help.
As explained in the docs, StreamBuilder is a:
Widget that builds itself based on the latest snapshot of interaction with a Stream.
So, to use it, first you need to create a Stream that provides your data, pass it to the stream prop of StreamBuilder, then in the builder prop you build your widget based on the snapshot data.
Here is a short example that uses Stream.periodic to return the future every 5 seconds and yield the future call:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class PeriodicRequester extends StatelessWidget {
Stream<http.Response> getRandomNumberFact() async* {
yield* Stream.periodic(Duration(seconds: 5), (_) {
return http.get("http://numbersapi.com/random/");
}).asyncMap((event) async => await event);
}
#override
Widget build(BuildContext context) {
return StreamBuilder<http.Response>(
stream: getRandomNumberFact(),
builder: (context, snapshot) => snapshot.hasData
? Center(child: Text(snapshot.data.body))
: CircularProgressIndicator(),
);
}
}

Flutter: how to mock Bloc

I would like to mock my Bloc in order to test my view.
For example, this is my Bloc:
class SearchBloc extends Bloc<SearchEvent, SearchState> {
#override
// TODO: implement initialState
SearchState get initialState => SearchStateUninitialized();
#override
Stream<SearchState> mapEventToState(SearchState currentState, SearchEvent event) async* {
if (event is UserWrites) {
yield (SearchStateInitialized.success(objects);
}
}
}
And this is the view:
class _SearchViewState extends State<SearchView> {
final TextEditingController _filterController = new TextEditingController();
#override
void initState() {
_filterController.addListener(() {
widget._searchBloc.dispatch(FetchByName(_filterController.text));
}
}
TextField buildAppBarTitle(BuildContext context) {
return new TextField(
key: Key("AppBarTextField"),
controller: _filterController,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: buildAppBarTitle(context),),
body: buildBlocBuilder(),
);
}
BlocBuilder<SearchEvent, SearchState> buildBlocBuilder() {
return BlocBuilder(
bloc: widget._searchBloc,
builder: (context, state) {
if (state is SearchStateUninitialized) {
return Container(
key: Key("EmptyContainer"),
);
}
return buildInitializedView(state, context);
}
});
buildInitializedView(SearchStateInitialized state, BuildContext context) {
if (state.objects.isEmpty) {
return Center(child: Text("Nothing found"),);
} else {
return buildListOfCards();
}
}
}
Now, this is my test:
testWidgets('Should find a card when the user searches for something', (WidgetTester tester) async {
_searchView = new SearchView(_searchBloc);
when(mockService.find( name: "a")).thenAnswer((_) =>
[objects]);
await tester.pumpWidget(generateApp(_searchView));
await tester.enterText(find.byKey(Key("searchBar")), "a");
await tester.pump();
expect(find.byType(Card), findsOneWidget);
});
}
As you can see, I just want to test that, when the user writes something in the search, and the object he's looking for exists, a card should be shown.
If I understood correctly, you are mocking some service that is used by the searchBloc. I personally try to design the app in a way that the app only depends on a bloc and the bloc may depend on some other services. Then when I would like to make a widget test, I only need to mock the bloc. You can use bloc_test package for that.
There is this example on the bloc_test page for stubbing a counterBloc:
// Create a mock instance
final counterBloc = MockCounterBloc();
// Stub the bloc `Stream`
whenListen(counterBloc, Stream.fromIterable([0, 1, 2, 3]));
however, I often do not need to stub the bloc stream and it is enough to emit the state, like this
when(counterBloc.state).thenAnswer((_) => CounterState(456));
Hope this helps.
Have a look at a post from David Anaya which deal with Unit Testing with “Bloc” and mockito.
The last version of his example is here
Sometimes widgets require a little time to build. Try with:
await tester.pumpWidget(generateApp(_searchView));
await tester.enterText(find.byKey(Key("searchBar")), "a");
await tester.pump(Duration(seconds: 1));
expect(find.byType(Card), findsOneWidget);
To mock the bloc, you can use the bloc_test package
Also, you may watch this tutorial which covers bloc testing include mock bloc very nice.

GWT code-splitting pattern for ClientBundle image resources

In my GWT large project, I have a ClientBundle for my image resources. I defined about 40 GIF files inside it. (size of each file is about 5KB)
Then I create a class with a static method to set the proper image to the obj that get as parameters:
public static void setImageFromId (String id,final Image img) {
//for 1.gif
if (id.equals("1")) {
GWT.runAsync(new RunAsyncCallback() {
#Override
public void onFailure(Throwable reason) {}
#Override
public void onSuccess() {
img.setResource(MyImages.INSTANCE.img1()); //MyImages is the ClientBundle
}
});
}
}
//for 2.gif
if (id.equals("2")) {
GWT.runAsync(new RunAsyncCallback() {
#Override
public void onFailure(Throwable reason) {}
#Override
public void onSuccess() {
img.setResource(MyImages.INSTANCE.img2()); //MyImages is the ClientBundle
}
});
}
//etc. for other images 3, 4, 5, ...
//...
}
I want to know is it good pattern for code-splitting? because if I don't do it all the 40 files will be cached to client browser in first call, but it is not necessary.
RGDS
So you're trying to avoid downloading each image when your page loads. That's good, if you don't know ahead of time whether every image will be needed.
But, what your code is doing is using code-splitting to only download the code to display your images when the image is needed, which as you can see, is only one line of code per image.
Try this code:
if (id.equals("1")) {
img.setSrc(MyImages.INSTANCE.img1().getUrl());
} else if (id.equals("2")) {
//.. and so on.
}
Your images will only be downloaded and displayed when the relevant image is needed. You can use Firebug or Chrome's Developer Tools to see when your images are being downloaded, they should only be requested when needed.
If you have any more questions or find that all your images are being downloaded on page load, let me know and I'll edit my answer again to help you out.