ในที่นี้จะพูดถึง point operation ก่อน ซึ่งเราได้เจอมาแล้วในหัวข้อก่อนๆ เช่น การเปลี่ยนรูปสีให้เป็นรูปสีเทา หรือ การทำ image negative
หากเราต้องการประมวลผลแต่ละพิกเซล ในแต่ละ channel RGBA ส่วนใหญ่การประมวลผลก็จะอยู่ในรูปแบบ
output = alpha*input + beta
เมื่อ output คือเมตริกซ์ผลลัพธ์ และ input คือ เมตริกซ์ตั้งต้น ส่วน alpha และ beta ก็จะเป็นตัวคูณและตัวบวกเพิ่ม เราอาจเรียกการคำนวณนี้ว่า Matrix Scaling
เมื่อค่า alpha และ beta มีค่าต่างๆกัน จะให้ผลลัพธ์ดังตัวอย่างต่อไปนี้
- alpha = 1 และ beta != 0 จะเป็นการเพิ่มหรือลดความสว่าง (brightness) ของรูป
- alpha >0 และ beta = 0 จะเป็นการเพิ่มหรือลดค่า contrast ของรูป
- alpha = -1 และ beta = 255 เป็นการทำ image negative
input.converTo(Mat output, int type, double alpha, double beta);
โดยที่ type ก็จะเป็นชนิดของเมตริกซ์ผลลัพธ์ที่ต้องการ เช่น CvType.CV_8UC4 เป็นต้น หรือกำหนดให้เป็นค่าติดลบเช่น -1 ก็ได้ ถ้าอยากให้เมตริกซ์ผลลัพธ์เป็นชนิดเดียวกับเมตริกซ์ตั้งต้น
ข้อดีของคำสั่งนี้ใน OpenCV คือมันจะปรับค่าของพิกเซลให้อยู่ในช่วง 0-255 โดยอัตโนมัติ
เราจะมาลองใช้คำสั่งนี้กันอีกครั้ง เพื่อปรับค่า brightness และ contrast ของรูป ดังนี้
โค้ดก็ประมาณนี้ครับ
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:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:id="@+id/ivImage" app:srcCompat="@drawable/lenna256" /> <RadioGroup android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/ivImage" android:layout_centerHorizontal="true" android:layout_marginTop="25dp" android:orientation="horizontal" android:id="@+id/rgroup"> <RadioButton android:text="Origin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rbtOrigin" android:layout_weight="1" android:checked="true" /> <RadioButton android:text="Brightness" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rbtBright" android:layout_weight="1" /> <RadioButton android:text="Contrast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/rbtContrast" 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; public class MainActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener{ private Bitmap bitmap; private ImageView ivImage; private RadioGroup rgroup; 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.lenna256); } public void adjust(double alpha, double beta) { //source matrix, unsigned 8-bit 4 channels (RGBA) Mat mat1 = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC4); //convert bitmap to matrix //Be careful that the output Mat have the same size as the input Bitmap //and of the 'CV_8UC4' type, RGBA format. Utils.bitmapToMat(bitmap, mat1); //output matrix, grayscale 8-bit 1 channel Mat mat2 = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC3); //Method 1: use built-in class, fastest //increase brightness or contrast or both //out = alpha*(in) + beta mat1.convertTo(mat2, CvType.CV_8UC3, alpha, beta); //Method 2: single loop //it requires converting mat data type from byte to double //otherwise the mat.get() gives the wrong pixel value // mat1.convertTo(mat1, CvType.CV_64FC4); // //create temp arrays to keep pixels for both original and output // double[] source = new double[(int)(mat1.total()*mat1.channels())]; // double[] dest = new double[(int)(mat2.total()*mat2.channels())]; // // //dump matrix to array // mat1.get(0, 0, source); // int j=0; // double alpha=1, beta=100; // //source RGBA, move every four bytes // for(int i=0;i<source.length;i+=4) { // //change brightness and contrast // dest[j] = alpha*source[i] + beta; // dest[j+1] = alpha*source[i+1] + beta; // dest[j+2] = alpha*source[i+2] + beta; // //dest RGB, mover every three bytes // j+=3; // } // //put the modified temp array to matrix, this step will clamp the pixel to <= 255 // mat2.put(0, 0, dest); //create bitmap having width (columns) and height(rows) as matrix and in the format of 8 bit/pixel //ARGB_8888 and RGB565 are normal bitmap formats Bitmap result = Bitmap.createBitmap(mat2.cols(), mat2.rows(), Bitmap.Config.RGB_565); //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.rbtOrigin) { ivImage.setImageResource(R.drawable.lenna256); } else if(id==R.id.rbtBright) { adjust(1,100); } else if(id==R.id.rbtContrast) { adjust(2,0); } } }
No comments:
Post a Comment