Click here to Skip to main content
15,888,579 members
Articles / Web Development / HTML
Tip/Trick

How to Establish a Different Picture of the Android Camera by Image Processing (Gray and Comic)?

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
19 Jan 2015CPOL2 min read 26.8K   544   6   2
To establish a different picture of the Android camera by image processing (Gray and Comic)

Image 1

Image 2

Introduction

Sometimes, we just find it fun to take a picture. Let’s take a different funny picture. We can develop it by ourselves. 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, we wait for the auto focus to finish when we take a picture. That is why we need to overwrite auto focus method to help us take a clear picture.
Secondly, while the image processing computes, we need to transform raw data to bitmap format. We used bitmap factory to help us do this transform.
Thirdly, there are two image processing that are 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 dividing 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 using 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 are 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 it is preparing auto focus and taking a picture of callback function. You just follow this architecture and then add new image effect. In this study, this app will open your camera before you choose image effect and then you choose 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 tip to help beginners in the camera app development. All code is presented in this tip. I need you to be a little patient to read. And then, you will find that out so easily.

Background

Equipment Operation System: Android 4.1.2 Development Utility: Eclipse ADT

Using the Code

File Name: AndroidManifest.xml

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

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.299f * 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.299f * 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();
    }
}

Exception

  • For debug, you should enable USB debug with your Android phone.

Acknowledgement

Thank you (Android, Eclipse) very much for this great development utility.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Instructor / Trainer GJun Information Co.,Ltd.
Taiwan Taiwan
Dr. Lai Tai-Yu received the Ph.D. from National Taipei University of Technology in 2016. He is the co-author of 2 journal, 25 conference papers, 6 books, 9 articles in the codeproject.com, and 2 patents inventions. His research interests lie in the areas of digital image processing and AI. He is a lecturer.  

Dr Taiyu Lai. Stopień doktora, uzyskał w 2016 roku i objął stanowisko adiunkta na Wydziale. Ponadto jest autorem i współautorem: 2 publikacji artykułu w czasopiśmie naukowym, 25 artykułów opublikowanych w materiałach konferencyjnych, 6 książek, 9 eseje w monografiach codeproject.com, i posiada dwa patenty. Zainteresowania naukowe: przetwarzania obrazów i AI. Jest wykładowcą.  

赖岱佑在2016年取得台北科技大学资讯工程博士学位,有2篇期刊、25篇会议论文、6本书籍、9篇文章在 codeproject.com 、2项专利。研究方向:数字图像处理和AI。现职是一位讲师。

賴岱佑在2016年取得台北科技大學資訊工程博士學位,有2篇期刊、25篇會議論文、6本書籍、9篇文章在 codeproject.com 、2項專利。研究方向:數位影像處理和AI。現職是一位講師。

Comments and Discussions

 
QuestionCamera rotation Pin
h_sedghi20-Jan-15 16:43
h_sedghi20-Jan-15 16:43 
AnswerRe: Camera rotation Pin
Lai Taiyu20-Jan-15 21:20
professionalLai Taiyu20-Jan-15 21:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.