พูดง่ายๆก็คือ การกลับสีรูปคือการคำนวณ 255-pixel นั่นเอง
ลองดูขั้นตอนกันครับ
สมมติว่าเราจะใช้ interface ง่ายๆเหมือนตัวอย่างที่แล้ว
<?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" /> <ToggleButton android:text="ToggleButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:id="@+id/tbProcess" android:layout_below="@+id/ivImage" android:layout_centerHorizontal="true" android:onClick="process" /> </RelativeLayout>
ในส่วนของโค้ด ก็จะมีขั้นตอนหลักๆคล้ายตัวอย่างที่แล้ว ผมจะขอสรุปย่อๆดังนี้
1. สร้าง matrix สำหรับรูปต้นฉบับและรูปผลลัพธ์ mat1 และ mat2
Mat mat1 = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC4);
Mat mat2 = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC3);
สังเกตว่าในที่นี้ matrix ผลลัพธ์เป็นชนิด CV_8UC3 เพราะเราจะไม่ใช้ alpha channel
2. กลับสีรูป ทำได้อย่างน้อยสามวิธีเช่น
2.1 ใช้คำสั่งสำเร็จรูป
จะใช้
Core.bitwise_not(mat1, mat2);
หรือmat1.convertTo(mat2, CvType.CV_8UC3, -1, 255);ก็ได้ มีความหมายเหมือนเอา 255 ไปลบทุก pixel
2.2 วนลูปแต่และแถวแต่ละคอลัมน์
int row = mat2.rows(); int col = mat2.cols(); for(int r=0;r<row;r++) { for(int c=0;c<col;c++) { //read pixel double[] pixelSet = mat1.get(r, c); //find average of RGB double[] pixel = new double[3]; pixel[0] = 255-pixelSet[0]; pixel[1] = 255-pixelSet[1]; pixel[2] = 255-pixelSet[2]; //put average value of pixel to matrix mat2.put(r, c, pixel); } }
2.3 dump matrix มาใส่ array
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; //source RGBA, move every four bytes for(int i=0;i<source.length;i+=4) { //negative dest[j] = 255-source[i]; dest[j+1] = 255-source[i+1]; dest[j+2] = 255-source[i+2]; //dest RGB, mover every three bytes j+=3; } //put the modified temp array to matrix mat2.put(0, 0, dest);
โค้ดรวมๆก็เป็นแบบนี้ครับ
import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.ToggleButton; import org.opencv.android.OpenCVLoader; import org.opencv.android.Utils; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.imgproc.Imgproc; public class MainActivity extends AppCompatActivity { private Bitmap bitmap; private ToggleButton tbProcess; private ImageView ivImage; 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); tbProcess = (ToggleButton) findViewById(R.id.tbProcess); //decode resource file to bitmap with no scale bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lenna256); //if you don't want Android to auto resize the bitmap according to device resolution // BitmapFactory.Options option = new BitmapFactory.Options(); // option.inScaled = false; // bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lenna256, option); } public void process(View view) { if(!tbProcess.isChecked()) { ivImage.setImageResource(R.drawable.lenna256); return; } //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 3 channels Mat mat2 = new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC3); //negative or inverse (255-pixel) //Method 1: use built-in class, fastest // Core.bitwise_not(mat1, mat2); //out = a(in) + b = -(in) + 255 = 255 - in // mat1.convertTo(mat2, CvType.CV_8UC3, -1, 255); //Method 2: 2D array, simple but slow // int row = mat2.rows(); // int col = mat2.cols(); // for(int r=0;r<row;r++) { // for(int c=0;c<col;c++) { // //read pixel // double[] pixelSet = mat1.get(r, c); // //find average of RGB // double[] pixel = new double[3]; // pixel[0] = 255-pixelSet[0]; // pixel[1] = 255-pixelSet[1]; // pixel[2] = 255-pixelSet[2]; // //put average value of pixel to matrix // mat2.put(r, c, pixel); // } // } //Method 3: single loop, more complex but faster //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; //source RGBA, move every four bytes for(int i=0;i<source.length;i+=4) { //negative dest[j] = 255-source[i]; dest[j+1] = 255-source[i+1]; dest[j+2] = 255-source[i+2]; //dest RGB, mover every three bytes j+=3; } //put the modified temp array to matrix 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); } }
No comments:
Post a Comment