如何在Android中将opencv Mat坐标转换为布局坐标?

问题描述 投票:0回答:1

我正在借助opencv在android中构建图像裁剪器。该应用程序从相机意图中捕获图像,并被用于基于图像徽标的具有预定边界的裁剪活动。问题是我无法显示精确到图像的预定边界。

需要比例因子,它将用于将opencv的mat坐标转换为布局坐标以准确显示边界。

实际结果-> https://ibb.co/5WDxCqR

预期结果

绿色边界矩形必须出现在“能源”徽标上

在opencv上发现了类似的问题,但没有提供解决方案(https://answers.opencv.org/question/186911/how-to-convert-mat-coordinates-to-layout-coordinates-in-android/

package com.example.ccsim.view

import android.app.Activity
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.util.Log
import android.view.MotionEvent
import android.widget.FrameLayout
import com.example.ccsim.processor.Corners
import com.example.ccsim.processor.SourceManager
import org.opencv.core.Point
import org.opencv.core.Size

class PaperRectangle : FrameLayout {
    constructor(context: Context) : super(context)
    constructor(context: Context, attributes: AttributeSet) : super(context, attributes)
    constructor(context: Context, attributes: AttributeSet, defTheme: Int) : super(context, attributes, defTheme)

    private val rectPaint = Paint()
    private val circlePaint = Paint()
    private var ratioX: Double = 1.0
    private var ratioY: Double = 1.0
    private var xOffset: Double = 1.0
    private var yOffset: Double = 1.0
    private var ratio: Double = 1.0
    private var tl: Point = Point()
    private var tr: Point = Point()
    private var br: Point = Point()
    private var bl: Point = Point()
    private val path: Path = Path()
    private var point2Move = Point()
    private var cropMode = false
    private var latestDownX = 0.0F
    private var latestDownY = 0.0F

    init {
        rectPaint.color = Color.GREEN
        rectPaint.isAntiAlias = true
        rectPaint.isDither = true
        rectPaint.strokeWidth = 6F
        rectPaint.style = Paint.Style.STROKE
        rectPaint.strokeJoin = Paint.Join.ROUND    // set the join to round you want
        rectPaint.strokeCap = Paint.Cap.ROUND      // set the paint cap to round too
        rectPaint.pathEffect = CornerPathEffect(10f)

        circlePaint.color = Color.RED
        circlePaint.isDither = true
        circlePaint.isAntiAlias = true
        circlePaint.strokeWidth = 4F
        circlePaint.style = Paint.Style.STROKE
    }

    fun onCorners2Crop(
        corners: Corners?,
        size: Size?
    ) {

        cropMode = true
        tl = corners?.corners?.get(0) ?: SourceManager.defaultTl
        tr = corners?.corners?.get(1) ?: SourceManager.defaultTr
        br = corners?.corners?.get(2) ?: SourceManager.defaultBr
        bl = corners?.corners?.get(3) ?: SourceManager.defaultBl
        val displayMetrics = DisplayMetrics()
        (context as Activity).windowManager.defaultDisplay.getMetrics(displayMetrics)
        //exclude status bar height
        val statusBarHeight = getStatusBarHeight(context)
        val navigationBarHeight = getNavigationBarHeight(context)
        val width1 = size?.width ?: 1.0
        val height1 = size?.height ?: 1.0
        val width2 = (displayMetrics.widthPixels).toDouble()
        val height2 = (displayMetrics.heightPixels - navigationBarHeight).toDouble()
        ratioX = width2.div(width1)
        ratioY = height2.div(height1)
        Log.i("*****Ratio X",ratioX.toString())
        Log.i("*****Ratio Y",ratioY.toString())
        Log.i("*****width 1",width1.toString())
        Log.i("*****height 1",height1.toString())
        Log.i("*****width 2",width2.toString())
        Log.i("*****height 2",height2.toString())
        Log.i("******BEFORE RESIZE","*******")
        Log.i("******tl",tl.toString())
        Log.i("******tr",tr.toString())
        Log.i("******br",br.toString())
        Log.i("******bl",bl.toString())
        reverseSize()
        Log.i("******After RESIZE","*******")
        Log.i("******tl",tl.toString())
        Log.i("******tr",tr.toString())
        Log.i("******br",br.toString())
        Log.i("******bl",bl.toString())
        movePoints()
    }

    fun getCorners2Crop(): List<Point> {
        resize()
        return listOf(tl, tr, br, bl)
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.drawPath(path, rectPaint)
        if (cropMode) {
            canvas?.drawCircle(tl.x.toFloat(), tl.y.toFloat(), 20F, circlePaint)
            canvas?.drawCircle(tr.x.toFloat(), tr.y.toFloat(), 20F, circlePaint)
            canvas?.drawCircle(bl.x.toFloat(), bl.y.toFloat(), 20F, circlePaint)
            canvas?.drawCircle(br.x.toFloat(), br.y.toFloat(), 20F, circlePaint)
        }
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {

        if (!cropMode) {
            return false
        }
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {
                latestDownX = event.x
                latestDownY = event.y
                calculatePoint2Move(event.x, event.y)
            }
            MotionEvent.ACTION_MOVE -> {
                point2Move.x = (event.x - latestDownX) + point2Move.x
                point2Move.y = (event.y - latestDownY) + point2Move.y
                movePoints()
                latestDownY = event.y
                latestDownX = event.x
            }
        }
        return true
    }

    private fun calculatePoint2Move(downX: Float, downY: Float) {
        val points = listOf(tl, tr, br, bl)
        point2Move = points.minBy { Math.abs((it.x - downX).times(it.y - downY)) } ?: tl
    }

    private fun movePoints() {
        path.reset()
        path.moveTo(tl.x.toFloat(), tl.y.toFloat())
        path.lineTo(tr.x.toFloat(), tr.y.toFloat())
        path.lineTo(br.x.toFloat(), br.y.toFloat())
        path.lineTo(bl.x.toFloat(), bl.y.toFloat())
        path.close()
        invalidate()
    }


    private fun resize() {
        tl.x = tl.x.div(ratioX)
        tl.y = tl.y.div(ratioY)
        tr.x = tr.x.div(ratioX)
        tr.y = tr.y.div(ratioY)
        br.x = br.x.div(ratioX)
        br.y = br.y.div(ratioY)
        bl.x = bl.x.div(ratioX)
        bl.y = bl.y.div(ratioY)
    }

    private fun reverseSize() {
        tl.x = tl.x.times(ratioX)
        tl.y = tl.y.times(ratioY)
        tr.x = tr.x.times(ratioX)
        tr.y = tr.y.times(ratioY)
        br.x = br.x.times(ratioX)
        br.y = br.y.times(ratioY)
        bl.x = bl.x.times(ratioX)
        bl.y = bl.y.times(ratioY)
    }

    private fun getNavigationBarHeight(pContext: Context): Int {
        val resources = pContext.resources
        val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
        return if (resourceId > 0) {
            resources.getDimensionPixelSize(resourceId)
        } else 0
    }

    private fun getStatusBarHeight(pContext: Context): Int {
        val resources = pContext.resources
        val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
        return if (resourceId > 0) {
            resources.getDimensionPixelSize(resourceId)
        } else 0
    }
}
java android opencv kotlin opencv4android
1个回答
0
投票

因此,您可以通过Android JNI使用OpenCV C ++绑定。这样就没有这种问题,导致Android相机和OpenCV计算相同的图像缓冲区。例如,阅读this文章。

© www.soinside.com 2019 - 2024. All rights reserved.