- https://en.wikipedia.org/wiki/Otsu's_method
- https://www.ncbi.nlm.nih.gov/pubmed/70454
ถ้าจะใช้งานสองวิธีนี้ใน OpenCV ก็ต้องเตรียม matrix ของรูปต้นฉบับให้เป็นแบบ CV_8UC1 หรือแบบ 8-bit 1 channel เสียก่อน (ใน reference บอกไว้) ดังนั้น เมื่อแปลง bitmap ให้เป็น Mat ด้วยคำสั่ง
Utils.bitmapToMat(bitmap, mat);
เมตริกซ์ mat ที่ได้จะเป็นแบบ CV_8UC4 โดยอัตโนมัติ เราจึงจำเป็นต้องแปลงให้เป็นแบบที่เราต้องการผ่านคำสั่ง
Imgproc.cvtColor(mat, mat1, Imgproc.COLOR_RGB2GRAY, 1);
จากนั้นค่อยมากำหนดการทำ thresholding โดยทั้งสองวิธีข้างต้นจะใช้ควบคู่กับการทำ thresholding 5 รูปแบบก่อนหน้านี้ เช่น ถ้าเราต้องการทำ binary thresholding โดยใช้ Otsu's method เพื่อกำหนดค่า threshold โดยอัตโนมัติก็จะใช้คำสั่ง
Imgproc.threshold(mat1, mat2, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
ให้สังเกตการกำหนดค่าพารามิเตอร์จะพบว่า ค่า threshold เรากำหนดให้เป็น 0 เพราะว่าเดี๋ยววิธีของ Otsu จะคำนวณให้เอง ส่วนวิธีการทำ thresholding ก็จะเป็นสองแบบรวมกัน เลยใช้รูปแบบ Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU ครับ
โค้ดทั้งหมดก็จะประมาณนี้
xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.mobile.opencv102.MainActivity"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/ivImage" app:srcCompat="@drawable/lenna_gray" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_weight="1" /> <RadioGroup android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rgroup" android:layout_marginTop="24dp" android:layout_below="@+id/ivImage" android:layout_centerHorizontal="true"> <RadioButton android:text="Original" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rOriginal" android:layout_weight="1" android:checked="true" /> <RadioButton android:text="Binary Otsu" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rBinaryOtsu" android:layout_weight="1" /> <RadioButton android:text="Binary Triangle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rBinaryTriangle" android:layout_weight="1" /> </RadioGroup> </RelativeLayout>
java
import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.ImageView; import android.widget.RadioGroup; import org.opencv.android.OpenCVLoader; import org.opencv.android.Utils; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.imgproc.Imgproc; public class MainActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener{ private Bitmap bitmap, result; private ImageView ivImage; private RadioGroup rgroup; private Mat mat1, mat2; static { if(OpenCVLoader.initDebug()) { Log.i("OpenCV", "Success"); } else { Log.i("OpenCV", "Fail"); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ivImage = (ImageView) findViewById(R.id.ivImage); rgroup = (RadioGroup) findViewById(R.id.rgroup); rgroup.setOnCheckedChangeListener(this); //decode resource file to bitmap with no scale bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lenna_gray); //temporary matrix to get pixel values from bitmap Mat temp = new Mat(); //convert bitmap to matrix, this matrix will always be of type CV_8UC4 Utils.bitmapToMat(bitmap, temp); //source matrix, for Otsu and Triangle thresholding must be unsigned 8-bit 1 channel (gray) mat1 = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC1); //convert temp matrix to mat1 of type CV_8U1, 1 channel Imgproc.cvtColor(temp, mat1, Imgproc.COLOR_RGB2GRAY, 1); //output matrix, grayscale 8-bit 1 channel mat2 = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC1); //create bitmap having width (columns) and height(rows) as matrix and in the format of 8 bit/pixel result = Bitmap.createBitmap(mat2.cols(), mat2.rows(), Bitmap.Config.RGB_565); } @Override public void onCheckedChanged(RadioGroup radioGroup, int id) { if(id==R.id.rOriginal) { ivImage.setImageResource(R.drawable.lenna_gray); return; } else if(id==R.id.rBinaryOtsu) { Imgproc.threshold(mat1, mat2, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); } else if(id==R.id.rBinaryTriangle) { Imgproc.threshold(mat1, mat2, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_TRIANGLE); } //convert result matrix to bitmap Utils.matToBitmap(mat2, result); //show bitmap in ImageView ivImage.setImageBitmap(result); } }
Hi, I am very interested in your work. Are you available for freelance work? If so, please email me at austin.syn.kim@gmail.com to discuss things further. We can communicate in Thai, if needed.
ReplyDeleteThank you,
Austin