My barcode reader runs well but I want user can only scan barcode what is inside in view area to detect. I wrote a code for it. I can get cornerPoints
of barcode that scanned but I can't realize if it's inside my detection view. I can't see rect
value of view
that passed from fragment to BarcodeAnalyzer class in debug mode. I don't know wether it is problem to achieve this or not.
BarcodeReaderFragment.kt
val imageAnalyzer = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
.also {
val rect = Rect()
binding.viewFocusArea.getGlobalVisibleRect(rect)
it.setAnalyzer(
cameraExecutor, BarcodeAnalyzer(
rect,
::barcodeListener
)
)
}
BarcodeAnalyzer.kt
class BarcodeAnalyzer(
private val rectArea: Rect,
private val barcodeListener: (barcode: String) -> Unit
) : ImageAnalysis.Analyzer {
.addOnCompleteListener {
image.close()
if (it.isSuccessful) {
val barcodes = it.result as List<Barcode>
// Task completed successfully
for (barcode in barcodes) {
val cornerTL = barcode.cornerPoints?.get(0)
val cornerTR = barcode.cornerPoints?.get(1)
val cornerBL = barcode.cornerPoints?.get(2)
val cornerBR = barcode.cornerPoints?.get(3)
cornerTL?.let { topLeft ->
cornerTR?.let { topRight ->
cornerBR?.let { bottomRight ->
cornerBL?.let { bottomLeft ->
if (rectArea.contains(topLeft) && rectArea.contains(topRight) && rectArea.contains(bottomRight) && rectArea.contains(
bottomLeft
)
) {
barcodeListener(barcode.rawValue ?: "")
}
}
}
fragment_barcode_reader.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.camera.view.PreviewView
android:id="@+id/preview_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<View
android:id="@+id/viewRightTopVCorner"
android:layout_width="12dp"
android:layout_height="1dp"
android:layout_marginEnd="-4dp"
android:layout_marginBottom="5dp"
android:background="@color/white"
app:layout_constraintBottom_toTopOf="@+id/viewFocusArea"
app:layout_constraintEnd_toEndOf="@+id/viewFocusArea" />
<View
android:id="@+id/viewRightTopHCorner"
android:layout_width="1dp"
android:layout_height="12dp"
android:background="@color/white"
app:layout_constraintEnd_toEndOf="@+id/viewRightTopVCorner"
app:layout_constraintTop_toBottomOf="@+id/viewRightTopVCorner" />
<View
android:id="@+id/viewRightBottomVCorner"
android:layout_width="12dp"
android:layout_height="1dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="-4dp"
android:background="@color/white"
app:layout_constraintEnd_toEndOf="@+id/viewFocusArea"
app:layout_constraintTop_toBottomOf="@+id/viewFocusArea" />
<View
android:id="@+id/viewRightBottomHCorner"
android:layout_width="1dp"
android:layout_height="12dp"
android:background="@color/white"
app:layout_constraintEnd_toEndOf="@+id/viewRightBottomVCorner"
app:layout_constraintBottom_toTopOf="@+id/viewRightBottomVCorner" />
<View
android:id="@+id/viewLeftBottomVCorner"
android:layout_width="12dp"
android:layout_height="1dp"
android:layout_marginStart="-4dp"
android:layout_marginTop="5dp"
android:background="@color/white"
app:layout_constraintStart_toStartOf="@+id/viewFocusArea"
app:layout_constraintTop_toBottomOf="@+id/viewFocusArea" />
<View
android:id="@+id/viewLeftBottomHCorner"
android:layout_width="1dp"
android:layout_height="12dp"
android:background="@color/white"
app:layout_constraintBottom_toTopOf="@+id/viewLeftBottomVCorner"
app:layout_constraintStart_toStartOf="@+id/viewLeftBottomVCorner" />
<View
android:id="@+id/viewLeftTopVCorner"
android:layout_width="12dp"
android:layout_height="1dp"
android:layout_marginStart="-4dp"
android:layout_marginBottom="5dp"
android:background="@color/white"
app:layout_constraintBottom_toTopOf="@+id/viewFocusArea"
app:layout_constraintStart_toStartOf="@+id/viewFocusArea" />
<View
android:id="@+id/viewLeftTopHCorner"
android:layout_width="1dp"
android:layout_height="12dp"
android:background="@color/white"
app:layout_constraintTop_toBottomOf="@+id/viewLeftTopVCorner"
app:layout_constraintStart_toStartOf="@+id/viewLeftTopVCorner" />
<View
android:id="@+id/viewFocusArea"
android:layout_width="0dp"
android:layout_height="101dp"
android:layout_marginStart="48dp"
android:layout_marginTop="35dp"
android:layout_marginEnd="47dp"
android:background="#00FFFFFF"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/preview_view"
app:layout_constraintTop_toBottomOf="@+id/txtBarcodeScan" />
<View
android:id="@+id/shadow_top"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="-4dp"
android:background="#80000000"
app:layout_constraintBottom_toTopOf="@+id/viewFocusArea"
app:layout_constraintEnd_toStartOf="@+id/shadow_right"
app:layout_constraintStart_toEndOf="@+id/shadow_left"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="invisible" />
<View
android:id="@+id/shadow_bottom"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="-4dp"
android:background="#80000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/shadow_right"
app:layout_constraintStart_toEndOf="@+id/shadow_left"
app:layout_constraintTop_toBottomOf="@+id/viewFocusArea"
tools:visibility="invisible" />
<View
android:id="@+id/shadow_left"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="-4dp"
android:background="#80000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/viewFocusArea"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="invisible" />
<View
android:id="@+id/shadow_right"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="-4dp"
android:background="#80000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/preview_view"
app:layout_constraintStart_toEndOf="@+id/viewFocusArea"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="invisible" />
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
app:layout_constraintEnd_toEndOf="@+id/preview_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_identity_hexagon" />
<TextView
android:id="@+id/txtBarcodeScan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Lütfen fiyatını öğrenmek istediğin \nürünün barkodunu okut"
android:textColor="@color/white"
android:gravity="center"
android:fontFamily="@font/poppins_regular"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnChangeShop"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="55dp"
android:backgroundTint="@color/dull_orange"
android:fontFamily="@font/poppins_semibold"
android:insetTop="0dp"
android:insetBottom="0dp"
android:letterSpacing="0"
app:cornerRadius="4dp"
android:paddingVertical="15dp"
android:text="@string/click_to_change_shop"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Just in case you still need it.
I've coded a snippet to test the transformation of the imageProxy coordinates to the preview coordinates using the Java version of the snippet found at :https://developer.android.com/training/camerax/transform-output.
The barcode is detected when its view enters the rectangle view located at height = previewView.getHeight()/5 and width = preview.getWidth()/2.
Conclusion : the detection works very well.
@SuppressLint("SetTextI18n")
@Override
public void analyze(@NonNull ImageProxy imageProxy) {
Image mediaImage = imageProxy.getImage();
int rotation = imageProxy.getImageInfo().getRotationDegrees();
if (mediaImage != null) {
InputImage image =
InputImage.fromMediaImage(mediaImage, rotation);
scanner.process(image)
.addOnSuccessListener(barcodes -> {
for (Barcode barcode : barcodes) {
processBarcodeResult(barcode, imageProxy);
}
})
.addOnCompleteListener(results -> imageProxy.close());
}
}
static void processBarcodeResult(Barcode barcode, ImageProxy imageProxy) {
// xp and yp are the coordinates of the center of the preview rectangle
int xp = previewView.getWidth() >> 1;
int yp = previewView.getHeight() / 5;
// xOffset and yOffset are threshold for detecting
// that the barcode view enters the preview rectangle
int xOffset = (int) (barcode.getBoundingBox().width() / 2.5f);
int yOffset = (int) (barcode.getBoundingBox().height() / 2.5f);
// xb and yb are the coordinates of the center
// of the barcode boundingbox.
int xb = barcode.getBoundingBox().centerX();
int yb = barcode.getBoundingBox().centerY();
// matrix transforms the imageProxy coordinates in the
// preview coordinates
Matrix matrix = getMappingMatrix(imageProxy);
float[] xyb = new float[]{xb, yb};
matrix.mapPoints(xyb);// transforms the bounding box center
// coordinates
xb = (int) xyb[0];
yb = (int) xyb[1];
if (xb > xp - xOffset && xb < xp + xOffset && yb > yp - yOffset && yb < yp + yOffset) {
format.setText(barcode.getFormat() + "");
code.setText(barcode.getRawValue());
// code and format are 2 TextView in the mainactivity layout.
}
}
static Matrix getMappingMatrix(ImageProxy imageProxy) {
Rect cropRect = imageProxy.getCropRect();
Matrix matrix = new Matrix();
// A float array of the source vertices (crop rect) in clockwise order.
float[] source = {cropRect.left, cropRect.top, cropRect.right,
cropRect.top, cropRect.right, cropRect.bottom, cropRect.left,
cropRect.bottom
};
// A float array of the destination vertices in clockwise order.
float[] destination = {0f, 0f, previewView.getWidth(), 0f,
previewView.getWidth(), previewView.getHeight(), 0f,
previewView.getHeight()
};
// The destination vertexes need to be shifted based on rotation degrees.
// The rotation degree represents the clockwise rotation needed to correct
// the image.
// Each vertex is represented by 2 float numbers in the vertices array.
int vertexSize = 2;
// The destination needs to be shifted 1 vertex for every 90° rotation.
// imageProxy is already rotated => rotationDegrees = 0!
int rotationDegrees = 0;
int shiftOffset = rotationDegrees / 90 * vertexSize;
float[] tempArray = destination.clone();
for (int toIndex = 0; toIndex < source.length; toIndex++) {
int fromIndex = (toIndex + shiftOffset) % source.length;
destination[toIndex] = tempArray[fromIndex];
}
matrix.setPolyToPoly(source, 0, destination, 0, 4);
return matrix;
}
I assume that you want to detect the barcode when it enters this ImageView!
You must determine the coordinates of the center of this image. These coordinates are the xp and yp coordinates I use in my snippet above.
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
app:layout_constraintEnd_toEndOf="@+id/preview_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_identity_hexagon" />