Implementing Stickers on an Image in Phimpme

One feature we implemented in the Phimpme photo editing application is to enable users to add stickers on top of the image. In this blog, I will explain how stickers are implemented in Phimpme. Features of Stickers Users can resize the stickers. Users can place the stickers wherever in the canvas. Step.1-Storing the Stickers in assets folder In Phimpme I stored the stickers in the assets folder. To distribute the stickers in different categories I made different folders according to the categories namely type1, type2, type3, type4 and so on.   Displaying stickers We used onBindViewHolder to Display the stickers in different categories like: Facial Express Objects Comments Wishes Emojis Hashtags String path will get the position of the particular type of stickers collection. This type is then loaded to the ImageLoader with the specific icon associating with the types.    @Override public void onBindViewHolder(mRecyclerAdapter.mViewHolder holder, final int position) {   String path = pathList.get(position);       ImageLoader.getInstance().displayImage("assets://" + path,holder.icon, imageOption);       holder.itemView.setTag(path);       holder.title.setText("");   int size = (int) getActivity().getResources().getDimension(R.dimen.icon_item_image_size_filter_preview);   LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(size,size);   holder.icon.setLayoutParams(layoutParams);   holder.itemView.setOnClickListener(new View.OnClickListener() {       @Override       public void onClick(View v) {           String data = (String) v.getTag();           selectedStickerItem(data);       }   }); } Step.2- Applying a sticker on the image When a particular sticker is selected selectedStickerItem() function is called.This function calls StickerView class to add the Bitmap on the image. It sends the path of the sticker as a parameter.   public void selectedStickerItem(String path) {   mStickerView.addBitImage(getImageFromAssetsFile(path)); } In StickerView class the image of the sticker is then converted into a Bitmap. It creates an object(item) of StickerItem class. This object calls the init function, which handles the size of the sticker and the placement of the sticker on the image. public void addBitImage(final Bitmap addBit) {   StickerItem item = new StickerItem(this.getContext());   item.init(addBit, this);   if (currentItem != null) {       currentItem.isDrawHelpTool = false;   }   bank.put(++imageCount, item);   this.invalidate(); } Step.3-Resizing the Sticker in the canvas A bitmap or any image has two axes namely x and y. We can resize the image using matrix calculation. float c_x = dstRect.centerX(); float c_y = dstRect.centerY(); float x = this.detectRotateRect.centerX(); float y = this.detectRotateRect.centerY(); We then calculate the source length and the current length: float srcLen = (float) Math.sqrt(xa * xa + ya * ya); float curLen = (float) Math.sqrt(xb * xb + yb * yb); Then we calculate the scale. This is required to calculate the zoom ratio. float scale = curLen / srcLen; We need to rescale the bitmap. That is if the user rotates the sticker or zoom in or zoom out the sticker. A helpbox surrounds the stickers showing the actual size of the sticker. This helpbox which is rectangular shape helps in resizing the sticker. RectUtil.scaleRect(this.dstRect, scale);// Zoom destination rectangle // Recalculate the Toolbox coordinates helpBox.set(dstRect); updateHelpBoxRect();// Recalculate rotateRect.offsetTo(helpBox.right - BUTTON_WIDTH, helpBox.bottom       - BUTTON_WIDTH); deleteRect.offsetTo(helpBox.left - BUTTON_WIDTH, helpBox.top       - BUTTON_WIDTH); detectRotateRect.offsetTo(helpBox.right - BUTTON_WIDTH, helpBox.bottom       - BUTTON_WIDTH); detectDeleteRect.offsetTo(helpBox.left - BUTTON_WIDTH, helpBox.top       - BUTTON_WIDTH); Conclusion In Phimpme a user can now place the sticker on top of the image. Resize the sticker, that is Zoom…

Continue ReadingImplementing Stickers on an Image in Phimpme

Integrating OpenCV for Native Image Processing in Phimpme Android

OpenCV is an open source computer vision library written in C and C++. It has many pre-built image processing functions for all kinds of purposes. The filters and image enhancing functions which we implemented in Phimpme are not fully optimized. So we decided to integrate the Android SDK of OpenCV in the Phimpme Android application. For integrating OpenCV in the application, the files inside the OpenCV Android SDK have to be properly placed inside the project. The build and make files of the project have to be configured correspondingly. Here, in this post, I will mention how we fully integrated OpenCV Android SDK and configured the Phimpme project. Setting Up First, we downloaded the OpenCV-Android-SDK zip from the official release page here. Now we extracted the OpenCV-Android-SDK.zip file to navigated to SDK folder to see that there are three folders inside it. etc, Java and native. All the build files are present inside the native directory and are necessarily copied to our project. The java directory inside the SDK folder is an external gradle module which has to imported and added as a dependency to our project. So we copied all the files from jni directory of SDK folder inside OpenCV-Android-SDK to jni directory of the Phimpme project inside main. Similarly copied the 3rdparty directory into the main folder of the Phimpme project. Libs folder should also be copied into the main folder of the Phimpme but it has to be renamed to jniLibs. The project structure can be viewed in the below image. Adding Module to Project Now we headed to Android Studio and right clicked on the app in the project view, to add a new module. In that window select import Gradle project and browse to the java folder inside the SDK folder of OpenCV-Android-SDK as shown in figures. Now we opened the build.gradle file of app level (not project level) and added the following line as a dependency.  compile project(':openCVLibrary320') Now open the build.gradle inside the openCVLibrary320 external module and change the platform tools version and  SDK versions. Configuring Native Build Files We removed all files related to cmake be we use ndk-build to compile native code. After removing the cmake files, added following lines in the application.mk file APP_OPTIM := release APP_ABI := all APP_STL := gnustl_static APP_CPPFLAGS := -frtti -fexceptions APP_PLATFORM := android-25 Now we further added the following lines to the top of Android.mk file OPENCV_INSTALL_MODULES:=on OPENCV_CAMERA_MODULES:=on OPENCV_LIB_TYPE := STATIC include $(LOCAL_PATH)/OpenCV.mk The third line here is a flag indicating that the library has to be static and there is no need for external OpenCVLoader application. Finally, find this line OPENCV_LIBS_DIR:=$(OPENCV_THIS_DIR)/../libs/$(OPENCV_TARGET_ARCH_ABI) in OpenCV.mk file and replace it with OPENCV_LIBS_DIR:=$(OPENCV_THIS_DIR)/../jniLibs/$(OPENCV_TARGET_ARCH_ABI) And finally, add the following lines to java part. So that the OpenCV is initialized and linked to application static {   if (!OpenCVLoader.initDebug()) {       Log.e( TAG + " - Error", "Unable to load OpenCV");   } else {       System.loadLibrary("modulename_present_in_Android.mk");   } } With this, the integration of OpenCV and the configuration of the project is completed.…

Continue ReadingIntegrating OpenCV for Native Image Processing in Phimpme Android

Passing Java Bitmap Object to Native for Image Processing and Getting it back in Phimpme Android

To perform any image processing operations on an image, we must have an image object in native part like we have a Bitmap object in Java. We cannot just pass the image bitmap object directly as an argument to native function because ‘Bitmap’ is a java object and C/C++ cannot interpret it directly as an image. So, here I’ll discuss a method to send java Bitmap object to Native part for performing image processing operations on it, which we implemented in the image editor of Phimpme Android Image Application. C/C++ cannot interpret java bitmap object. So, we have to find the pixels of the java bitmap object and send them to native part to create a native bitmap over there. In Phimpme, we used a “struct” data structure in C to represent native bitmap. An image has three color channels red, green, blue. We consider alpha channel(transparency) for an argb8888 type image. But in Phimpme, we considered only three channels as it is enough to manipulate these channels to implement the edit functions, which we used in the Editor of Phimpme. We defined a struct, type-defined as NativeBitmap with attributes corresponding to all the three channels. We defined this in the nativebitmap.h header file in the jni/ folder of Phimpme so that we can include this header file in c files in which we needed to use a NativeBitmap struct. #ifndef NATIVEBITMAP #define NATIVEBITMAP #endif typedef struct { unsigned int width; unsigned int height; unsigned int redWidth; unsigned int redHeight; unsigned int greenWidth; unsigned int greenHeight; unsigned int blueWidth; unsigned int blueHeight; unsigned char* red; unsigned char* green; unsigned char* blue; } NativeBitmap; void deleteBitmap(NativeBitmap* bitmap); int initBitmapMemory(NativeBitmap* bitmap, int width, int height); As I explained in my previous post here on introduction to flow of native functions in Phimpme-Android, we defined the native functions with necessary arguments in Java part of Phimpme. We needed the image bitmap to be sent to the native part of the Phimpme application. So the argument in the function should have been java Bitmap object. But as mentioned earlier, the C code cannot interpret this java bitmap object directly as an image. So we created a function for getting pixels from a bitmap object in the Java class of Phimpme application. This function returns an array of unsigned integers which correspond to the pixels of a particular row of the image bitmap. The array of integers can be interpreted by C, so we can send this array of integers to native part and create a receiving native function to create NativeBitmap. We performed image processing operations like enhancing and applying filters on this NativeBitmap and after the processing is done, we sent the array of integers corresponding to a row of the image bitmap and constructed the Java Bitmap Object using those received arrays in java. The integers present in the array correspond to the color value of a particular pixel of the image bitmap. We used this color value to get red, green…

Continue ReadingPassing Java Bitmap Object to Native for Image Processing and Getting it back in Phimpme Android

Applying Filters on Images using Native Functions in Phimpme Android

In the Phimpme application, the user can apply multiple colorful filters on images captured from application’s camera or already available images on the device. This application of filters on images is performed using native image processing functions. We implemented many filters for enhancing the image. Implementation of few of the filter functions is shown below. Filters are applied to an image by modifying the color values of pixels in the Phimpme application. This is similar to the implementation of image enhancing functions in the editor of Phimpme. My post on that is available here. Black and White filter: Black and white filter can be called as gray scaling the image. In a gray scale image, there will only be a single color channel. If multiple channels are present, the corresponding pixel values in all channels will be same. Here in Phimpme, we have an RGB image. It has 3 color channels. Every pixel has three values. Black and white filter can be implemented by replacing those three different values with the average of those values. The implementation of the function and the resultant image with the comparison is shown below. void applyBlackAndWhiteFilter(Bitmap* bitmap) {  register unsigned int i;  unsigned int length = (*bitmap).width * (*bitmap).height;  register unsigned char grey;  unsigned char* red = (*bitmap).red;  unsigned char* green = (*bitmap).green;  unsigned char* blue = (*bitmap).blue;  for (i = length; i--;) {     grey = (red[i] + green[i] + blue[i]) / 3;     red[i] = truncate((int) grey);     green[i] = truncate((int) grey);     blue[i] = truncate((int) grey);  } }      Ansel Filter This Ansel Filter is a monotone filter present in Phimpme which is similar to black and white. Here in this filter, the contrast will be little high and gives the image artistic look. This is achieved in Phimpme by hard overlaying the gray pixel components of the image. The rest is same as the black and white filter. The implementation of hard overlay blending and the Ansel function is shown below with the resultant images. static unsigned char hardLightLayerPixelComponents(unsigned char maskComponent, unsigned char imageComponent) {  return (maskComponent > 128) ? 255 - (( (255 - (2 * (maskComponent-128)) ) * (255-imageComponent) )/256) : (2*maskComponent*imageComponent)/256; } void applyAnselFilter(Bitmap* bitmap) { /*initializations*/  unsigned char br,bg,bb;  for (i = length; i--; ) {       grey = (red[i] + green[i] + blue[i]) / 3;       int eff = hardLightLayerPixelComponents(grey, grey);       red[i] = truncate(eff);       green[i] = truncate(eff);       blue[i] = truncate(eff);  } }      Sepia Filter The Sepia Filter in Phimpme results in a monotone image with orangish yellow tone. Its implementation uses pre-defined look up tables(LUTs) for all the three channels. The luminosity of a particular pixel is found out and then the red, green, blue values are found out from the look up tables(LUTs) corresponding to that luminosity. The look up table arrays we used for the sepia effect in Phimpme are given below and the implementation is also shown below. const unsigned char sepiaRedLut[256] = {24, 24, 25, 26, 27, 28, 29, 30, 30, 30, 31, 32, 33,…

Continue ReadingApplying Filters on Images using Native Functions in Phimpme Android