IT/Android

[Android] Android으로 OpenCV 사용하기 - 3

버건디 팩토리 2021. 8. 3. 02:46
반응형

MainActivity

아래의 모든 코딩은 class MainActivity : AppCompatActivity()의 TODO에서 진행한다.

class MainActivity : AppCompatActivity() { // TODO }

기본 설정

activity_main과 연결하기

모든 안드로이드 코딩의 시작이다.
findViewByID로 view와 MainActivity의 모든 성분을 연결한다.

private val imageView: ImageView by lazy {
        findViewById<ImageView>(R.id.imageView)
    }
    private val leftButton: AppCompatButton by lazy {
        findViewById<AppCompatButton>(R.id.leftButton)
    }
    private val rightButton: AppCompatButton by lazy {
        findViewById<AppCompatButton>(R.id.rightButton)
    }

Asset

Asset 함수를 만들어 gray로 변경할 이미지를 미리 넣어준다.
추후 이름을 유지하며 갤러리 내 이미지를 붙여넣는 방식으로 원하는 이미지를 흑백으로 바꿔보려고 한다.

폴더 설정

file - new - folder - assets folder
를 통해 asset 폴더를 생성한다.


그럼 이렇게 assets 함수가 추가되게 된다.
해당 assets 폴더에 미리 이미지 10개를 추가한다.


img_xxxx의 형태로 이미지를 추가했다.
참고로 assets 폴더는
오른쪽 클릭 - Show in Explorer
를 통해 바로 윈도우 창으로 열 수 있다.

Gradle 압축 방지

png 파일 압축 방지를 위해 build.gradle내에 아래 code를 추가한다.

android {
	...
	aaptOptions {
	        noCompress 'png'
	}
}

Assets 폴더 내 파일 불러오기

setImage

setImage 함수를 만들어 assets 내 이미지를 불러온다.
num은 보여질 수 있는 이미지의 개수를, index는 현재 show하는 이미지의 index를 의미한다.

private val num: Int = 10
    private var index: Int = 0
    private lateinit var bitmap: Bitmap

    private fun setImage(index: Int) {
        val inputStream: InputStream
        var image = Mat()

        val name: String = "img_" + String.format("%04d", index) + ".png"
        Log.d("MainActivity", "name : $name")           // name 확인을 위한 출력
        inputStream = assets.open(name)                             // name에 해당하는 파일 open

        bitmap = BitmapFactory.decodeStream(inputStream)
        Utils.bitmapToMat(
            bitmap,
            image
        )

        // Gray 이미지 변경 과정 진행
        image = getGrayImage(image)

        Utils.matToBitmap(image, bitmap)
        // view에 이미지를 설정한다.
        imageView.setImageBitmap(bitmap)
    }

cvtColor는 이미지를 흑백으로 변경한다.
그럼 이제 이런 식으로 이미지가 출력되는 것을 볼 수 있다.

Button으로 이미지 교체

Button에 따른 num 변환 (MainActivity.kt)

left, right 버튼이 클릭될 때 마다 이제 num을 변경하며 다른 이미지를 출력해보겠다.
global value index가 현재 이미지의 번호를 나타내므로
left 버튼이 눌릴 때 index--를, right 버튼이 눌릴 때 index++를 진행한다.
이때 범위 내 이미지만을 출력해야 하므로 아래와 같이 에러 방지를 진행한다.

fun getLeftButton(view: View) {
        index--
        if(index < 0) {
            Toast.makeText(this, "첫번째 이미지입니다.", Toast.LENGTH_SHORT).show()
            index = 0
        }
        Log.d("MainActivity", "index : $index")

        setImage(index)
    }
    fun getRightButton(view: View) {
        index++
        if(index >= num) {
            Toast.makeText(this, "마지막 이미지입니다.", Toast.LENGTH_SHORT).show()
            index = num - 1
        }
        Log.d("MainActivity", "index : $index")

        setImage(index)
    }

Button에 따른 num 변환 (activity_main)

setOnClickListner를 사용해도 되지만 쉬운 사용을 위하여 xml 내 adroid:onClick 을 통해 바로 함수를 불러와 실행한다.

<androidx.appcompat.widget.AppCompatButton
        android:id="@+id/leftButton"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:background="#40000000"
        android:onClick="getLeftButton"
        android:text="left"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

<androidx.appcompat.widget.AppCompatButton
        android:id="@+id/rightButton"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:background="#40000000"
        android:onClick="getRightButton"
        android:text="right"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

onCreate

첫 이미지 초기화

마지막으로 처음 화면이 실행될 때 이미지 초기화도 해야하므로 onCreate 함수 내에 setImage(index) 함수를 추가한다.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    setImage(index)
}

Result

이와 같이 진행한다면 left, right 값에 따라 이미지가 변하는 App을 만들 수 있다.
현재는 가장 쉬운 GrayScale로 이미지를 출력했지만 SIFT, Gaussian Blur 등 다양한 양상으로 Asset 내 이미지를 변경해 볼 수 있다.

마무리

아무래도 첫 글이라 많이 어렵고 중구난방이었던 것 같다...
누가 이 글을 읽을지는 모르겠지만 정리가 잘 되어있지 않아 많이 미안하다.
조금 모듈화하여서 조금씩 많이 올리는 방안을 생각해보아야 할 것 같다.

전체 code

activity_main

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/leftButton"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:background="#40000000"
        android:onClick="getLeftButton"
        android:text="left"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/rightButton"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:background="#40000000"
        android:onClick="getRightButton"
        android:text="right"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity

package edu.android.project.opencv_kotlin_test

class MainActivity : AppCompatActivity() {

    companion object {

        // Used to load the 'native-lib' library on application startup.
        init {
            if(!OpenCVLoader.initDebug()){
                Log.d("MainActivity", "OpenCV is not loaded!")
            } else {
                Log.d("MainActivity", "OpenCV is loaded successfully!")
            }

            System.loadLibrary("native-lib")
        }
    }

    private val imageView: ImageView by lazy {
        findViewById<ImageView>(R.id.imageView)
    }
    private val leftButton: AppCompatButton by lazy {
        findViewById<AppCompatButton>(R.id.leftButton)
    }
    private val rightButton: AppCompatButton by lazy {
        findViewById<AppCompatButton>(R.id.rightButton)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        setImage(index)
    }

    private val num: Int = 10
    private var index: Int = 0
    private lateinit var bitmap: Bitmap

    private fun setImage(index: Int) {
        val inputStream: InputStream
        var image = Mat()

        val name: String = "img_" + String.format("%04d", index) + ".png"
        Log.d("MainActivity", "name : $name")           // name 확인을 위한 출력
        inputStream = assets.open(name)                             // name에 해당하는 파일 open

        bitmap = BitmapFactory.decodeStream(inputStream)
        Utils.bitmapToMat(
            bitmap,
            image
        )

        // Gray 이미지 변경 과정 진행
        image = getGrayImage(image)

        Utils.matToBitmap(image, bitmap)
        // view에 이미지를 설정한다.
        imageView.setImageBitmap(bitmap)
    }

    private fun getGrayImage(img: Mat): Mat {
        Imgproc.cvtColor(img, img, Imgproc.COLOR_RGB2GRAY)
        return img
    }

    fun getLeftButton(view: View) {
        index--
        if(index < 0) {
            Toast.makeText(this, "첫번째 이미지입니다.", Toast.LENGTH_SHORT).show()
            index = 0
        }
        Log.d("MainActivity", "index : $index")

        setImage(index)
    }
    fun getRightButton(view: View) {
        index++
        if(index >= num) {
            Toast.makeText(this, "마지막 이미지입니다.", Toast.LENGTH_SHORT).show()
            index = num - 1
        }
        Log.d("MainActivity", "index : $index")

        setImage(index)
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    external fun stringFromJNI(): String
}
반응형