diff --git a/app/src/main/java/com/example/androidProjectOnTFLite/CapturePhotoFragment.kt b/app/src/main/java/com/example/androidProjectOnTFLite/CapturePhotoFragment.kt index c60e0f4..d9fb803 100644 --- a/app/src/main/java/com/example/androidProjectOnTFLite/CapturePhotoFragment.kt +++ b/app/src/main/java/com/example/androidProjectOnTFLite/CapturePhotoFragment.kt @@ -4,6 +4,7 @@ import android.app.Activity import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.graphics.drawable.BitmapDrawable import android.os.Bundle import android.provider.MediaStore import android.util.Log @@ -21,38 +22,28 @@ import java.io.ByteArrayOutputStream class CapturePhotoFragment : Fragment() { - private lateinit var camera: ImageView + private lateinit var camera: TextView private lateinit var btnCapturePhoto: Button - private lateinit var intent:Intent + private val intent= Intent(MediaStore.ACTION_IMAGE_CAPTURE) private lateinit var textClass:TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) - startActivityForResult( - intent, - CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) + startCamera() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - - // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_capture_photo, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - - btnCapturePhoto = btn_capture_photo camera = img_view_camera textClass = text_view_class btnCapturePhoto.setOnClickListener { - startActivityForResult( - intent, - CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) + startCamera() } } @@ -68,33 +59,33 @@ class CapturePhotoFragment : Fragment() { val stream = ByteArrayOutputStream() bmp!!.compress(Bitmap.CompressFormat.PNG, 100, stream) val byteArray: ByteArray = stream.toByteArray() - // convert byte array to Bitmap val bitmap = BitmapFactory.decodeByteArray( byteArray, 0, byteArray.size ) - camera.setImageBitmap(bitmap) - textClass.text = "Loading..." + camera.text="" + camera.setBackgroundDrawable((BitmapDrawable(resources, bitmap))) + textClass.text = "Classifying..." + val imageNetClasses = ImageClassification(bitmap, context!!).objectDetection() textClass.text = imageNetClasses btnCapturePhoto.visibility=View.VISIBLE - Thread { - val imgPath = - Utils.getImagePathFromBitmap(context!!, bitmap, title = imageNetClasses) - PictureDao?.insertPicture(Picture(imageNetClasses, imgPath)) - - //textClass.text = PictureDao!!.getPictures().toString() - db?.pictureDao()?.getPictures()?.forEach() { - Log.i("Fetch Records", "Id: : ${it.id}") - Log.i("Fetch Records", "Classes: : ${it.imageNetClasses}") - Log.i("Fetch Records", "Path: : ${it.imgPath}") - } - }.start() + val imgPath = Utils.getImagePathFromBitmap(context!!, bitmap, title = imageNetClasses) + db?.pictureDao()?.insertPicture(Picture(imageNetClasses, imgPath)) + } + else { + camera.text = "" + camera.background = resources.getDrawable(R.drawable.pytorch_logo) + btnCapturePhoto.visibility = View.VISIBLE } } } - + private fun startCamera(){ + startActivityForResult( + intent, + CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) + } companion object { private const val CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 1888 } diff --git a/app/src/main/java/com/example/androidProjectOnTFLite/ImageClassification.kt b/app/src/main/java/com/example/androidProjectOnTFLite/ImageClassification.kt index 20d64d8..66e8b0a 100644 --- a/app/src/main/java/com/example/androidProjectOnTFLite/ImageClassification.kt +++ b/app/src/main/java/com/example/androidProjectOnTFLite/ImageClassification.kt @@ -19,12 +19,11 @@ class ImageClassification(bitmap: Bitmap, context: Context) { ) public fun objectDetection(): String { - val outputTensor = module.forward(IValue.from(inputTensor)).toTensor() - val scores = outputTensor.dataAsFloatArray - val maxScoreIdx = scores.indices.maxBy { scores[it] } ?: -1 - val classNames = ImageNetClasses.IMAGENET_CLASSES[maxScoreIdx] - return classNames - } + val outputTensor = module.forward(IValue.from(inputTensor)).toTensor() + val scores = outputTensor.dataAsFloatArray + val maxScoreIdx = scores.indices.maxBy { scores[it] } ?: -1 + return ImageNetClasses.IMAGENET_CLASSES[maxScoreIdx] + } private fun loadModule(context: Context) = Module.load(Utils.assetGetAbsolutePathByName(context, "resnet18.pt")); diff --git a/app/src/main/java/com/example/androidProjectOnTFLite/ItemViewHolder.kt b/app/src/main/java/com/example/androidProjectOnTFLite/ItemViewHolder.kt new file mode 100644 index 0000000..f5e5bcd --- /dev/null +++ b/app/src/main/java/com/example/androidProjectOnTFLite/ItemViewHolder.kt @@ -0,0 +1,26 @@ +package com.example.androidProjectOnTFLite + +import android.net.Uri +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.example.androidProjectOnTFLite.RoomDB.Picture + + +class ItemViewHolder(inflater: LayoutInflater, parent: ViewGroup) : RecyclerView.ViewHolder(inflater.inflate(R.layout.item, parent, false)){ + + private var classes: TextView? = null + private var photo: ImageView? = null + + init{ + classes = itemView.findViewById(R.id.classesTextView) + photo = itemView.findViewById(R.id.photo) + } + + fun bind(pic: Picture){ + photo?.setImageURI(Uri.parse(pic.imgPath)) + classes?.text = pic.imageNetClasses + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/androidProjectOnTFLite/ListOfClassifiedImagesFragment.kt b/app/src/main/java/com/example/androidProjectOnTFLite/ListOfClassifiedImagesFragment.kt index 01e69e5..a44ac88 100644 --- a/app/src/main/java/com/example/androidProjectOnTFLite/ListOfClassifiedImagesFragment.kt +++ b/app/src/main/java/com/example/androidProjectOnTFLite/ListOfClassifiedImagesFragment.kt @@ -5,18 +5,24 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import com.example.androidProjectOnTFLite.RoomDB.Picture +import kotlinx.android.synthetic.main.fragment_list_of_classified_images.* -/** - * A simple [Fragment] subclass. - */ class ListOfClassifiedImagesFragment : Fragment() { - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_list_of_classified_images, container, false) } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + recyclerView.apply{ + layoutManager = LinearLayoutManager(activity) + adapter = RecyclerViewAdapter((db?.pictureDao()?.getPictures() as MutableList?)!!) + } + + } } diff --git a/app/src/main/java/com/example/androidProjectOnTFLite/MainActivity.kt b/app/src/main/java/com/example/androidProjectOnTFLite/MainActivity.kt index 3959987..b1f6f6d 100644 --- a/app/src/main/java/com/example/androidProjectOnTFLite/MainActivity.kt +++ b/app/src/main/java/com/example/androidProjectOnTFLite/MainActivity.kt @@ -16,10 +16,7 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - - //db = AppDatabase.getDatabaseInstance(this) - db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "pics-database") + db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "pics-database").allowMainThreadQueries() .build() - // val host = NavHostFragment.create(R.navigation.nav_graph) } } diff --git a/app/src/main/java/com/example/androidProjectOnTFLite/MenuFragment.kt b/app/src/main/java/com/example/androidProjectOnTFLite/MenuFragment.kt index 74e0d08..4111cb0 100644 --- a/app/src/main/java/com/example/androidProjectOnTFLite/MenuFragment.kt +++ b/app/src/main/java/com/example/androidProjectOnTFLite/MenuFragment.kt @@ -7,16 +7,11 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.navigation.Navigation import kotlinx.android.synthetic.main.fragment_menu.* - -/** - * A simple [Fragment] subclass. - */ class MenuFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_menu, container, false) } @@ -25,7 +20,4 @@ class MenuFragment : Fragment() { toCamera.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_menuFragment_to_capturePhotoFragment)) toList.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_menuFragment_to_listOfClassifiedImagesFragment)) } - - //val navController = - } diff --git a/app/src/main/java/com/example/androidProjectOnTFLite/RecyclerViewAdapter.kt b/app/src/main/java/com/example/androidProjectOnTFLite/RecyclerViewAdapter.kt new file mode 100644 index 0000000..25fa8bf --- /dev/null +++ b/app/src/main/java/com/example/androidProjectOnTFLite/RecyclerViewAdapter.kt @@ -0,0 +1,45 @@ +package com.example.androidProjectOnTFLite + +import android.net.Uri +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.example.androidProjectOnTFLite.RoomDB.Picture +import kotlinx.android.synthetic.main.item.view.* + +class RecyclerViewAdapter(val items: MutableList) + : RecyclerView.Adapter(){ + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) + : ViewHolder { + val v: View = LayoutInflater.from(parent.context) + .inflate(R.layout.item,parent,false) + return ViewHolder(v) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.image.setImageURI(Uri.parse(items[position].imgPath)) + holder.classes.text = items[position].imageNetClasses + holder.delete.setOnClickListener { deleteItem(position) } + } + + override fun getItemCount(): Int { + return items.size + } + + private fun deleteItem(position: Int){ + db?.pictureDao()?.delete(items[position]) //from db + items.removeAt(position) //from list + notifyItemRemoved(position) //from screen + notifyItemRangeChanged(position, itemCount) + } + class ViewHolder(itemView:View): RecyclerView.ViewHolder(itemView){ + val image: ImageView = itemView.photo + val classes: TextView = itemView.classesTextView + val delete: ImageButton = itemView.btnDelete + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/androidProjectOnTFLite/RoomDB/AppDatabase.kt b/app/src/main/java/com/example/androidProjectOnTFLite/RoomDB/AppDatabase.kt index 43d4e8b..bbf05c1 100644 --- a/app/src/main/java/com/example/androidProjectOnTFLite/RoomDB/AppDatabase.kt +++ b/app/src/main/java/com/example/androidProjectOnTFLite/RoomDB/AppDatabase.kt @@ -4,7 +4,6 @@ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase -import androidx.room.TypeConverters @Database( entities = [Picture::class], @@ -12,7 +11,6 @@ import androidx.room.TypeConverters exportSchema = false ) - abstract class AppDatabase : RoomDatabase() { abstract fun pictureDao(): PictureDao? diff --git a/app/src/main/java/com/example/androidProjectOnTFLite/RoomDB/Picture.kt b/app/src/main/java/com/example/androidProjectOnTFLite/RoomDB/Picture.kt index 401b5d8..aefac9f 100644 --- a/app/src/main/java/com/example/androidProjectOnTFLite/RoomDB/Picture.kt +++ b/app/src/main/java/com/example/androidProjectOnTFLite/RoomDB/Picture.kt @@ -1,5 +1,6 @@ package com.example.androidProjectOnTFLite.RoomDB +import android.net.Uri import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/app/src/main/java/com/example/androidProjectOnTFLite/Utils/Utils.kt b/app/src/main/java/com/example/androidProjectOnTFLite/Utils/Utils.kt index 52ea7d7..51c3f5f 100644 --- a/app/src/main/java/com/example/androidProjectOnTFLite/Utils/Utils.kt +++ b/app/src/main/java/com/example/androidProjectOnTFLite/Utils/Utils.kt @@ -1,6 +1,11 @@ package com.example.androidProjectOnTFLite.Utils + +import android.content.ContentValues.TAG import android.content.Context +import android.database.Cursor import android.graphics.Bitmap +import android.net.Uri +import android.os.Environment import android.provider.MediaStore import android.util.Log import java.io.ByteArrayOutputStream @@ -8,10 +13,9 @@ import java.io.File import java.io.FileOutputStream import java.io.IOException import java.text.SimpleDateFormat -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter import java.util.* + object Utils { fun assetGetAbsolutePathByName(context: Context, assetName: String): String? { val file = File(context.filesDir, assetName) @@ -32,6 +36,7 @@ object Utils { } return null } + fun getImagePathFromBitmap(context: Context, bitmap: Bitmap, title: String = "Title", description: String? = null): String { val bytes = ByteArrayOutputStream() val sdf = SimpleDateFormat("-dd.MM.yyyy.HH-mm-ss") @@ -39,4 +44,5 @@ object Utils { bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes) return MediaStore.Images.Media.insertImage(context.contentResolver, bitmap, fileName, description) } + } \ No newline at end of file diff --git a/app/src/main/res/drawable/del.xml b/app/src/main/res/drawable/del.xml new file mode 100644 index 0000000..60e64c5 --- /dev/null +++ b/app/src/main/res/drawable/del.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/drawable/pytorch_logo.png b/app/src/main/res/drawable/pytorch_logo.png new file mode 100644 index 0000000..bad49bf Binary files /dev/null and b/app/src/main/res/drawable/pytorch_logo.png differ diff --git a/app/src/main/res/layout/fragment_capture_photo.xml b/app/src/main/res/layout/fragment_capture_photo.xml index 8fecd5b..e97d72b 100644 --- a/app/src/main/res/layout/fragment_capture_photo.xml +++ b/app/src/main/res/layout/fragment_capture_photo.xml @@ -1,36 +1,41 @@ - + android:layout_marginTop="5dp" + android:textAlignment="center" + android:textSize="20sp" + app:layout_constraintTop_toTopOf="parent" /> - - - - - + android:layout_height="450dp" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp" + android:gravity="center" + android:text="Loading\n..." + android:textSize="60sp" + app:layout_constraintBottom_toTopOf="@+id/btn_capture_photo" + app:layout_constraintTop_toBottomOf="@+id/text_view_class" + tools:layout_editor_absoluteX="-16dp" />