How to Establish a Different Picture of the Android Camera by Image Processing (Gray and Comic)?
How to Establish a Different Picture of the Android Camera by Image Processing (Gray and Comic)?¶
Introduction
Sometimes, we just feel fun to take a picture. Let’s take a different funny picture. We can develop by self. To design gray and comic style picture, it is not hard. You just need to own an Android phone and you can establish a funny camera app.
Normally, you will encounter auto focus when you take a picture. This problem can be solved in this study. Firstly, while we can be waiting the auto focus to finish when we take a picture. That is why need to overwrite auto focus method to help us take a clear picture. Secondly, while the image processing had compute when we need to transform raw data to bitmap format. We used to bitmap factory to help us do this transform. Thirdly, there are two image processing that was chosen by users. The gray scale and comic effect in this study. The gray scale is very simple that is just getting R, G and B of amount and then divide by three. That is your new pixels of gray scale, but we still need to know how to get R, G and B of channel respectively. We are getting a pixel value after use the Color method to help us get R, G and B of values. There is a difficult convolution processing about comic effect. The comic progress is two processing, including Sobel filter and ordered dither processing. We need to combine the two results of the image processing to establish a new image, that is called comic effect.
If you want to develop more image effect, you could use this code of architecture. To keep developing your new idea. Because there are preparing auto focus and take a picture of callback function. You just follow this architecture and then to add new image effect.
In this study, this app will open your camera before you choice image effect and then you have chosen an effect gray scale or comic. You can press 'take a picture' button and then you can watch a processed image. Such as those figures.
To develop this app just only two files, It is very simple, that is why I wrote this article. Because to help beginners in the camera app development. All code presents this article. I need to, you're a little patient to read. And then you will find out that so easy.
Equipment
Operation System: Android 4.1.2
Development Utility: Eclipse ADT
Usage
File Name: AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.style.camera" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.style.camera.MainActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <! -- We should set those permissions. --> <uses-permission android:name="android.permission.CAMERA"></uses-permission> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> </manifest>
File Name: MainActivity.java public class MainActivity extends Activity implements SurfaceHolder.Callback { SurfaceView mSurfaceView ; Button btn_Capture; Camera mCamera; PictureCallback mPictureCB; AutoFocusCallback mAutoFocusCB; ImageView ImgView; TextView txtView; Bitmap bitmapClone; RadioGroup rdg_Main; RadioButton rdb_Gray; RadioButton rdb_Comic; int iImageProcessingId; @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Set the format of window, as per the PixelFormat types. // This overrides the default format that is selected by // the Window based on its window decorations. // System chooses a format that supports translucency (many alpha bits). getWindow().setFormat(PixelFormat.TRANSLUCENT); // Enable extended window features. // Flag for the "no title" feature, turning off the title at the top of the screen. requestWindowFeature(Window.FEATURE_NO_TITLE); // Set the flags of the window, as per the WindowManager.LayoutParams flags. // Window flag: Hide all screen decorations (e.g. status bar). // while this window is displayed. This allows the window to use // the entire display space for itself -- the status bar will be // hidden when an app window with this flag set is on the top layer. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // Set the activity content from a layout resource. // The resource will be inflated, adding all top-level views to the activity. setContentView(R.layout.activity_main); // Change the desired orientation of this activity. // If the activity is currently in the foreground or // otherwise impacting the screen orientation, // the screen will immediately be changed // (possibly causing the activity to be restarted). // Otherwise, this will be used the next time the activity is visible. // Constant corresponding to portrait in the android.R.attr.screenOrientation attribute. this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // -- (Start) ImgView = (ImageView)this.findViewById(R.id.ImgView); txtView = (TextView)this.findViewById(R.id.txtView); btn_Capture = (Button)this.findViewById(R.id.btn_Capture); mSurfaceView = (SurfaceView)this.findViewById(R.id.surView_Camera); rdg_Main = (RadioGroup) findViewById (R.id.rdg_Main); rdb_Gray = (RadioButton) findViewById (R.id.rdb_Gray); rdb_Comic = (RadioButton) findViewById (R.id.rdb_Comic); // -- (End) // Set and get SurfaceHolder // Abstract interface to someone holding a display surface. // Allows you to control the surface size and format, // edit the pixels in the surface, and monitor changes to the surface. // This interface is typically available through the SurfaceView class. // When using this interface from a thread other than the one running // its SurfaceView, you will want to carefully read the methods // lockCanvas and Callback.surfaceCreated(). SurfaceHolder mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.addCallback(this); mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // To record the choice of image processing by the user iImageProcessingId = 0; // To listen choice of the user and record it. // There are two choices, including Gray and Comic. rdg_Main.setOnCheckedChangeListener( new RadioGroup.OnCheckedChangeListener() {
@Override public void onCheckedChanged(RadioGroup group, int checkedId) { // TODO Auto-generated method stub // Gray if ( checkedId == rdb_Gray.getId() ) { iImageProcessingId = 0; } // Comic else if ( checkedId == rdb_Comic.getId() ) { iImageProcessingId = 1; } } }); // To establish Camera.takePicture callback function. mPictureCB = new PictureCallback(){ // Image processing. // Overwrite onPictureTake function. @Override public void onPictureTaken(byte[] data, Camera camera){ // We use the BitmapFactory to decode become raw data to bitmap format. Bitmap mBitmap = BitmapFactory.decodeByteArray(data, 0 , data.length); bitmapClone = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), mBitmap.getConfig()); bitmapClone.copy(mBitmap.getConfig(), true); // For debug of switch. if ( true ) { int iY = 0; int iX = 0; int iPixel = 0; int iRed = 0; int iGreen = 0; int iBlue = 0; int iRGBAvg = 0; // Gray of image processing. if ( iImageProcessingId == 0 ) { // The height of the image for ( iY = 0; iY < bitmapClone.getHeight(); iY++ ) { // The width of the image for ( iX = 0; iX < bitmapClone.getWidth(); iX++ ) { // To get pixel. iPixel = mBitmap.getPixel(iX, iY); // To get value of the red channel. iRed = Color.red(iPixel); // To get value of the green channel. iGreen = Color.green(iPixel); // To get value of the blue channel. iBlue = Color.blue(iPixel); // Compute value of gray. iRGBAvg = ( iRed + iGreen + iBlue ) / 3; // Set pixel of gray. bitmapClone.setPixel(iX, iY, Color.rgb(iRGBAvg, iRGBAvg, iRGBAvg)); } } } // Comic else if ( iImageProcessingId == 1 ) { // The horizontal of Sobel matrix int iSobel1 [][]= { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 }}; // The vertical of Sobel matrix int iSobel2 [][]= { { -1, 0, -1 }, { -2, 0, 2 }, { 1, 0, 1 }}; // The ordered dither of the matrix int iOrderDither [][] = { { 28, 255, 57 }, { 142, 113, 227 }, { 170, 198, 85 } }; float fYofYUV = 0.0f; int iYofYUV = 0; int iX1 = 0; int iY1 = 0; int iR = 1; int iValue1 = 0; int iValue2 = 0; int iValue = 0; // For a horizontal index of Sobel matrix int iX2 = 0; // For a vertical index of Sobel matrix int iY2 = 0; // Sobel filter of image processing // The height of the image for ( iY = 1; iY < bitmapClone.getHeight() - 1; iY++ ) { // The width of the image for ( iX = 1; iX < bitmapClone.getWidth() - 1; iX++ ) { iY2= 0; iValue1 = 0; iValue2 = 0; // The height of Sobel matrix for ( iY1 = iY - iR; iY1 <= iY + iR; iY1++ ) { iX2 = 0; // The width of Sobel matrix for ( iX1 = iX - iR; iX1 <= iX + iR; iX1++ ) { // Get value of pixel iPixel = mBitmap.getPixel(iX1, iY1); // To get value of the red channel. iRed = Color.red(iPixel); // To get value of the green channel. iGreen = Color.green(iPixel); // To get value of the blue channel. iBlue = Color.blue(iPixel); // Compute value of gray. fYofYUV = ( 0.299 * iRed ) + ( 0.587f * iGreen ) + ( 0.114f * iBlue ); // To compute Sobel matrix, we transform float to integer. iYofYUV = (int) fYofYUV; // Convolution computing horizontal of Sobel matrix. iValue1 += iYofYUV * iSobel1[iX2][iY2]; // Convolution computing vertical of Sobel matrix. iValue2 += iYofYUV * iSobel2[iX2][iY2]; iX2++; } iY2++; iX2 = 0; } // Choice maximum value. iValue = Math.max(iValue1, iValue2); // The twenty-four is a magic number. if ( iValue > 24 ) { // Set the pixel is black. bitmapClone.setPixel(iX, iY, Color.rgb(0, 0, 0)); } else { // Set the pixel is white. bitmapClone.setPixel(iX, iY, Color.rgb(255, 255, 255)); } } } // The height of the image. But we are stepping to three once. for ( iY = 0; iY < bitmapClone.getHeight() - 3; iY+=3 ) { // The width of the image. But we are stepping to three once. for ( iX = 0; iX < bitmapClone.getWidth() - 3; iX+=3 ) { iY2 = 0; // The height of the matrix. for ( iY1 = iY; iY1 <= iY + 2 ; iY1++ ) { iX2 = 0; // The width of the matrix. for ( iX1 = iX; iX1 <= iX + 2; iX1++ ) { // Get value of pixel iPixel = mBitmap.getPixel(iX1, iY1); // To get value of the red channel. iRed = Color.red(iPixel); // To get value of the green channel. iGreen = Color.green(iPixel); // To get value of the blue channel. iBlue = Color.blue(iPixel); // Compute value of gray. fYofYUV = ( 0.299 * iRed ) + ( 0.587f * iGreen ) + ( 0.114f * iBlue ); // To compute Sobel matrix, we transform float to integer. iYofYUV = (int) fYofYUV; // If the gray depth more than the matrix, it should be white. if ( iYofYUV >= iOrderDither[iX2][iY2] ) { bitmapClone.setPixel(iX, iY, Color.rgb(255, 255, 255)); } // Otherwise, it should be black. else { bitmapClone.setPixel(iX, iY, Color.rgb(0, 0, 0)); } iX2++; } iY2++; iX2 = 0; } } } } } // Set processed image to display. ImgView.setImageBitmap(bitmapClone); // Show the height of the image. String strInfo = ""; strInfo = String.valueOf(mBitmap.getHeight()); txtView.setText(strInfo); // Restart camera to preview. camera.startPreview(); // To disable auto focus of callback function. camera.autoFocus(null); } };
// To establish Camera.AutoFocusCallback mAutoFocusCB = new AutoFocusCallback(){ @Override public void onAutoFocus(boolean success, Camera camera){ // When auto focus is done and then we will take a picture. if ( success == true ) { // Into take a picture of callback function. camera.takePicture(null, null, mPictureCB); } } }; // While a user press the take a picture button, when it starts auto focus. btn_Capture.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try{ // To make sure the camera is open. if(mCamera != null){ // Create a thread. new Thread(new Runnable() { public void run() { // To execute the auto focus. mCamera.autoFocus(mAutoFocusCB); } }).start(); } }catch(Exception e) { e.printStackTrace(); } } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub // Get parameters of the camera. Camera.Parameters parameters = mCamera.getParameters(); // Set size of the picture. parameters.setPictureSize(640, 480); // Set size of preview. parameters.setPreviewSize(width, height); // Set auto focus. parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // Set parameters of the camera. mCamera.setParameters(parameters); // Start preview. mCamera.startPreview(); } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub // If the camera is initially successful and then to open camera. if ( mCamera == null ) { mCamera = Camera.open(); } try { // Set SurfaceHolder. mCamera.setPreviewDisplay(holder); }catch(Exception e) { e.printStackTrace(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub // Stop preview. mCamera.stopPreview(); // Release Camera. mCamera.release(); } }
You can download source code and binary code, the hyperlink is https://drive.google.com/file/d/0BzHb_OyLyVZlOFlFbVpOd0tRbVk/view?usp=sharing
MD5: 0bcefd51cf373542fae4456a9b1659eb
Exception
- For debug, you should enable USB debug with your android phone.
Refernce
None.
Acknowledge
Thank you (Android, Eclipse) very much for this great development utility.