ปกติแล้ว Thresholding มักจะใช้ในงาน segmentation คือแยกส่วนของภาพสีเทา ซึ่งมักจะแยกออกเป็นสองส่วนคือส่วนที่เราสนใจ (foreground) กับส่วนที่เราไม่สนใจ (background) ดังนั้นกระบวนการนี้จึงอาจถูกเรียกอีกชื่อว่า Binarization ก็ได้
ลองมาดูตัวอย่างผลลัพธ์ของการทำ Thresholding 5 แบบกันก่อนครับ
OpenCV มีคำสั่งในการทำ Thresholding คือ
public static double threshold(Mat src, Mat dst, double thresh, double maxval, int type)
-src และ dst คือ source และ destination matrix นั่นเอง
-thresh คือค่า threshold ที่เราเอาไว้แยกภาพ
-maxval คือค่าสูงสุดที่เราทำ thresholding แล้วอยากให้เป็น ปกติก็มักจะกำหนดให้เป็นค่าสูงสุดของภาพ เช่น 255
-type คือ วิธีการ thresholding มีด้วยกัน 7 แบบ ในที่นี้เราจะลอง 5 แบบ ได้แก่
(คัดลอกจาก http://docs.opencv.org/java/2.4.9/org/opencv/imgproc/Imgproc.html#threshold(org.opencv.core.Mat,%20org.opencv.core.Mat,%20double,%20double,%20int)
THRESH_BINARY
dst(x,y) = maxval if src(x,y) > thresh; 0 otherwise
THRESH_BINARY_INV
dst(x,y) = 0 if src(x,y) > thresh; maxval otherwise
THRESH_TRUNC
dst(x,y) = threshold if src(x,y) > thresh; src(x,y) otherwise
THRESH_TOZERO
dst(x,y) = src(x,y) if src(x,y) > thresh; 0 otherwise
THRESH_TOZERO_INV
dst(x,y) = 0 if src(x,y) > thresh; src(x,y) otherwise
ลองมาดูโค้ดกัน
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_alignParentBottom="true" 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" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rBinary" android:layout_weight="1" /> <RadioButton android:text="Binary, Inverted" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rBinaryInverted" android:layout_weight="1" /> <RadioButton android:text="Truncate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rTruncate" android:layout_weight="1" /> <RadioButton android:text="To zero" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rToZero" android:layout_weight="1" /> <RadioButton android:text="To zero, Inverted" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rToZeroInverted" android:layout_weight="1" /> </RadioGroup> <TextView android:text="Threshold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tvThreshold" android:textSize="18sp" android:layout_below="@+id/ivImage" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_marginTop="12dp" /> <SeekBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/sbThreshold" android:layout_alignBottom="@+id/tvThreshold" android:layout_toRightOf="@+id/tvThreshold" android:layout_alignRight="@+id/ivImage" android:layout_alignEnd="@+id/ivImage" android:max="255" android:progress="127" /> </RelativeLayout>
java
package com.example.mobile.opencv102; 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 android.widget.SeekBar; 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, SeekBar.OnSeekBarChangeListener{ private Bitmap bitmap, result; private ImageView ivImage; private RadioGroup rgroup; private SeekBar sbThreshold; private Mat mat1, mat2; private int threshold = 127; private int type = -1; 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); sbThreshold = (SeekBar) findViewById(R.id.sbThreshold); sbThreshold.setOnSeekBarChangeListener(this); //decode resource file to bitmap with no scale bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lenna_gray); //source matrix, unsigned 8-bit 4 channels (RGBA) mat1 = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC4); //convert bitmap to matrix Utils.bitmapToMat(bitmap, mat1); //output matrix, grayscale 8-bit 4 channels mat2 = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC4); //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); } public void adjust(double thres, int type) { //thresholding //public static double threshold(Mat src, Mat dst, double thresh, double maxval, int type) Imgproc.threshold(mat1, mat2, thres, 255, type); //convert result matrix to bitmap Utils.matToBitmap(mat2, result); //show bitmap in ImageView ivImage.setImageBitmap(result); } @Override public void onCheckedChanged(RadioGroup radioGroup, int id) { if(id==R.id.rOriginal) { ivImage.setImageResource(R.drawable.lenna_gray); type = -1; } else if(id==R.id.rBinary) { type = Imgproc.THRESH_BINARY; adjust(threshold, type); } else if(id==R.id.rBinaryInverted) { type = Imgproc.THRESH_BINARY_INV; adjust(threshold, type); } else if(id==R.id.rTruncate) { type = Imgproc.THRESH_TRUNC; adjust(threshold, type); } else if(id==R.id.rToZero) { type = Imgproc.THRESH_TOZERO; adjust(threshold, type); } else if(id==R.id.rToZeroInverted) { type = Imgproc.THRESH_TOZERO_INV; adjust(threshold, type); } } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { threshold = progress; if(type!=-1) { adjust(threshold, type); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }
อ้างอิง
- http://docs.opencv.org/3.2.0/db/d8e/tutorial_threshold.html
- http://docs.opencv.org/java/2.4.9/org/opencv/imgproc/Imgproc.html#threshold(org.opencv.core.Mat,%20org.opencv.core.Mat,%20double,%20double,%20int)
No comments:
Post a Comment