Notice
Recent Posts
Recent Comments
Today
Total
04-19 00:01
Archives
관리 메뉴

Jeongchul Kim

딥러닝을 활용한 패션 유사 상품 추천 서비스 (4) 메인, 앨범과 카메라에서 이미지 가져오기 본문

Android

딥러닝을 활용한 패션 유사 상품 추천 서비스 (4) 메인, 앨범과 카메라에서 이미지 가져오기

김 정출 2017. 6. 27. 19:23


 

딥러닝을 활용한 패션 유사 상품 추천 서비스 (4) 메인, 앨범과 카메라에서 이미지 가져오기


딥러닝을 활용한 패션 유사 상품 추천 서비스 (1) 기획

딥러닝을 활용한 패션 유사 상품 추천 서비스 (2) Android app 개발

딥러닝을 활용한 패션 유사 상품 추천 서비스 (3) Google Login 개발


본 프로젝트는 사용자가 직접 의류를 촬영하거나, 의류의 이미지로 검색하여 비슷한 상품을 추천하고 관련 쇼핑몰로 연계해주는 시스템을 개발하고자 한다. 디지털 이미지의 사용이 기하급수적으로 증가함에 따라, 텍스트에 의한 이미지 검색 방법이 한계에 이르게 되었다. 텍스트 검색은 키워드 기반 검색으로 가격, 브랜드, 색상 등과 같은 정형화된 속성만을 분류하여 찾을 수 있다. 시각적인 요소가 중요한 패션 분야에서는 텍스트 검색을 통해 찾은 결과가 사용자가 검색하려는 의도에 충족시키지 못한다. 텍스트 검색의 한계점을 보완하고자, 각 분야에서는 쉽게 제품 검색을 할 수 있도록 이미지 검색 기능과 유사한 이미지를 추천해주는 시스템을 개발하고 있다.


이를 위해 효과적으로 구축하기 위해서 딥러닝 기반의 컨볼루션 신경망(CNN; Convolutional Neural Network) 기술을 적용해 주어진 이미지 안에서 상품에 해당하는 영역을 자동으로 인식하고, 그 영역에서 검색에 필요한 각각의 요소(색상, 모양, 패턴)를 추출해 분류 과정을 거치고, 클러스터링 기법을 통해 가장 유사한 이미지 군을 추천하는 시스템을 개발한다.


프로젝트 환경

Android Studio 2.2.3, Android 4.4(KitKat) API 19


APP UI


MainActivity


MainActivity 개발을 진행해보겠습니다.

MainActivity에 Android에서 제공하는 슬라이딩 메뉴(Sliding)인 NavigationView를 사용하겠습니다.

activity_main.xml


<?xml version="1.0" encoding="utf-8"?>

<android.support.v4.widget.DrawerLayout 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/drawer_layout"

  android:layout_width="match_parent"

  android:layout_height="match_parent"

  android:fitsSystemWindows="true"

  tools:openDrawer="start">


  <include

      layout="@layout/app_bar_main"

      android:layout_width="match_parent"

      android:layout_height="match_parent" />


  <android.support.design.widget.NavigationView

      android:id="@+id/nav_view"

      android:layout_width="wrap_content"

      android:layout_height="match_parent"

      android:layout_gravity="start"

      android:fitsSystemWindows="true"

      app:headerLayout="@layout/nav_header_main"

      app:menu="@menu/activity_main_drawer" />


</android.support.v4.widget.DrawerLayout>


app_bar_main.xml

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout 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:layout_width="match_parent"

android:layout_height="match_parent"

android:fitsSystemWindows="true"

tools:context="kookmin.cs.msdj.forstyle.MainActivity">


<android.support.design.widget.AppBarLayout

  android:layout_width="match_parent"

  android:layout_height="wrap_content"

  android:theme="@style/AppTheme.AppBarOverlay">


  <android.support.v7.widget.Toolbar

      android:id="@+id/toolbar"

      android:layout_width="match_parent"

      android:layout_height="?attr/actionBarSize"

      android:background="?attr/colorPrimary"

      app:popupTheme="@style/AppTheme.PopupOverlay" />


</android.support.design.widget.AppBarLayout>


<include layout="@layout/content_main" />


</android.support.design.widget.CoordinatorLayout>

content_main.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/content_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"

  app:layout_behavior="@string/appbar_scrolling_view_behavior"

  tools:context="kookmin.cs.msdj.forstyle.MainActivity"

  tools:showIn="@layout/app_bar_main">


  <FrameLayout

      android:id="@+id/content_fragment_layout"

      android:layout_width="match_parent"

      android:layout_height="match_parent"/>

</RelativeLayout>

nav_header_main.xml

사이드 메뉴에 해당하는 nav_header_main을 수정합니다.


nav_header_main.xml에는 ImageView와 TextView가 2개 있습니다.

ImageView에는 사용자의 프로필 사진을 TextView에 첫 번째는 사용자의 이름을, 두 번째 TextView에는 사용자의 메일을 넣어보겠습니다.


<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  xmlns:app="http://schemas.android.com/apk/res-auto"

  android:layout_width="match_parent"

  android:layout_height="@dimen/nav_header_height"

  android:background="@drawable/side_nav_bar"

  android:gravity="bottom"

  android:orientation="vertical"

  android:paddingBottom="@dimen/activity_vertical_margin"

  android:paddingLeft="@dimen/activity_horizontal_margin"

  android:paddingRight="@dimen/activity_horizontal_margin"

  android:paddingTop="@dimen/activity_vertical_margin"

  android:theme="@style/ThemeOverlay.AppCompat.Dark">


  <ImageView

      android:id="@+id/personPhoto"

      android:layout_width="wrap_content"

      android:layout_height="wrap_content"

      android:paddingTop="@dimen/nav_header_vertical_spacing"

      app:srcCompat="@android:drawable/sym_def_app_icon" />


  <TextView

      android:id="@+id/personName"

      android:layout_width="match_parent"

      android:layout_height="wrap_content"

      android:paddingTop="@dimen/nav_header_vertical_spacing"

      android:text="Android Studio"

      android:textAppearance="@style/TextAppearance.AppCompat.Body1" />


  <TextView

      android:id="@+id/personEmail"

      android:layout_width="wrap_content"

      android:layout_height="wrap_content"

      android:text="android.studio@android.com" />


</LinearLayout>



MainActivity.java

import android.content.SharedPreferences;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.os.AsyncTask;

import android.os.Bundle;

import android.support.design.widget.FloatingActionButton;

import android.support.design.widget.Snackbar;

import android.view.View;

import android.support.design.widget.NavigationView;

import android.support.v4.view.GravityCompat;

import android.support.v4.widget.DrawerLayout;

import android.support.v7.app.ActionBarDrawerToggle;

import android.support.v7.app.AppCompatActivity;

import android.support.v7.widget.Toolbar;

import android.view.Menu;

import android.view.MenuItem;

import android.widget.ImageView;

import android.widget.TextView;


import com.google.android.gms.auth.api.Auth;

import com.google.android.gms.auth.api.signin.GoogleSignInOptions;

import com.google.android.gms.auth.api.signin.GoogleSignInResult;

import com.google.android.gms.common.api.GoogleApiClient;

import com.google.android.gms.common.api.OptionalPendingResult;


import java.io.IOException;

import java.io.InputStream;

import java.io.UnsupportedEncodingException;

import java.net.HttpURLConnection;

import java.net.URL;


public class MainActivity extends AppCompatActivity

      implements NavigationView.OnNavigationItemSelectedListener {


  private static final String TAG = "MainActivity";

  private GoogleSignInOptions gso;

  private GoogleApiClient mGoogleApiClient;

  private GoogleSignInResult result;


  // SharedPreference

  private SharedPreferences prefs;


  // Navi_veiw

  private View nav_view;


  // Navi-Component

  private ImageView personPhoto;

  private TextView personName;

  private TextView personEmail;


  //

  private Bitmap bit;


  @Override

  protected void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);

      setContentView(R.layout.activity_main);

      Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

      setSupportActionBar(toolbar);


      DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);

      ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(

              this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);

      drawer.setDrawerListener(toggle);

      toggle.syncState();

      // 슬라이딩 메뉴인 NavigationView

      NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);

      navigationView.setNavigationItemSelectedListener(this);

      nav_view = navigationView.getHeaderView(0);

      // NavigationView의 컴포넌트인 ImageView(사용자 프로필 이미지), TextView(이름과 메일주소)

      personPhoto = (ImageView) nav_view.findViewById(R.id.personPhoto);

      personName = (TextView) nav_view.findViewById(R.id.personName);

      personEmail = (TextView) nav_view.findViewById(R.id.personEmail);


      setNaviDrawerGooglePersonSync();

      FragmentTransaction ft = getSupportFragmentManager().beginTransaction();

      ft.replace(R.id.content_fragment_layout, new SearchImageFragment());

      ft.commit();


  }


  @Override

  public void onBackPressed() {

      DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);

      if (drawer.isDrawerOpen(GravityCompat.START)) {

          drawer.closeDrawer(GravityCompat.START);

      } else {

          super.onBackPressed();

      }

  }


  @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 boolean onOptionsItemSelected(MenuItem item) {

      // Handle action bar item clicks here. The action bar will

      // automatically handle clicks on the Home/Up button, so long

      // as you specify a parent activity in AndroidManifest.xml.

      int id = item.getItemId();


      //noinspection SimplifiableIfStatement

      if (id == R.id.action_settings) {

          return true;

      }


      return super.onOptionsItemSelected(item);

  }


  @SuppressWarnings("StatementWithEmptyBody")

  @Override

  public boolean onNavigationItemSelected(MenuItem item) {

  // Handle navigation view item clicks here.

      int id = item.getItemId();

      Fragment fragment = null; // 메뉴에 따라 이동할 Fragment


      if (id == R.id.nav_camera) {

          fragment = new SearchImageFragment();

          Log.d("MainActivity","SearchImageFragment");

       } else if (id == R.id.nav_gallery) {

           fragment = new ResultShowProductListFragment();

           Log.d("MainActivity","ResultShowProductListFragment");

       } else if (id == R.id.nav_slideshow) {

       } else if (id == R.id.nav_manage) {


       } else if (id == R.id.nav_share) {


       } else if (id == R.id.nav_send) {


       }


       if(fragment != null) {

           FragmentTransaction ft = getSupportFragmentManager().beginTransaction();

           ft.replace(R.id.content_fragment_layout, fragment);

           ft.commit();

       }


       DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);

       drawer.closeDrawer(GravityCompat.START);


       return true;

  }



  public void setNaviDrawerGooglePersonSync() {

      prefs = getSharedPreferences("GOOGLE_LOGIN",0); // SharedPreference 환경 변수 사용

      String email = prefs.getString("PERSON_EMAIL","None");

      String name = prefs.getString("PERSON_NAME","None");

      String photo_uri = prefs.getString("PERSON_PHOTO_URI","None");

      personEmail.setText(email);

      personName.setText(name);

      WebGetImage task = new WebGetImage();

      task.execute(photo_uri);

  }


  // 네트워크에 접속하여 이미지를 가져오는 클래스 선언

  class WebGetImage extends AsyncTask<String, Integer, Bitmap> {


      @Override

      protected Bitmap doInBackground(String... urls) {

          // 네트워크에 접속해서 데이터를 가져옴


          try {

              //웹사이트에 접속 (사진이 있는 주소로 접근)

              URL Url = new URL(urls[0]);

              // 웹사이트에 접속 설정

              HttpURLConnection conn = (HttpURLConnection)Url.openConnection();

              conn.setDoInput(true);

              // 연결하시오

              conn.connect();

              // 스트림 클래스를 이용하여 이미지를 불러옴

              InputStream is = conn.getInputStream();

              // 스트림을 통하여 저장된 이미지를 이미지 객체에 넣어줌

              bit = BitmapFactory.decodeStream(is);


          } catch (UnsupportedEncodingException e) {

              e.printStackTrace();

          } catch (IOException e) {

              e.printStackTrace();

          }

          return bit;

      }



      protected void onPostExecute (Bitmap bit) {

          personPhoto.setImageBitmap(bit); // 사용자의 프로필의 이미지를 ImageView 컴포넌트에 넣어줍니다.

      }


  }

}



SearchImageFragment

다음은 사용자가 앨범(Gallery)에서 이미지를 찾거나, 직접 사진을 촬영하고 생성된 이미지는 Crop을 통해 원하는 영역을 설정합니다.

최종으로 ImageView에서 크롭된 사진이 보여지도록 개발합니다.

Submit 버튼을 통해 서버로 전송되며, progress-bar를 통해 로딩 중임을 사용자에게 알린다.


fragment_search_image.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  android:layout_width="match_parent"

  android:layout_height="match_parent">

  <LinearLayout

      android:layout_width="match_parent"

      android:layout_height="match_parent"

      android:orientation="vertical">

      <TextView

          android:layout_width="match_parent"

          android:layout_height="wrap_content"

          android:text="사진 선택"

          android:layout_marginTop="20dp"

          android:textSize="40sp"

          android:gravity="center_horizontal"

          android:textColor="@color/colorBlack"/>

      <TextView

          android:layout_width="match_parent"

          android:layout_height="wrap_content"

          android:text="갤러리 또는 앨범에서 사진을 선택해주세요"

          android:textSize="15sp"

          android:gravity="center_horizontal"

          android:textColor="@color/black_overlay"/>

      <TextView

          android:layout_width="match_parent"

          android:layout_height="wrap_content"

          android:text="옷이 잘보이도록 크롭해주세요"

          android:textSize="15sp"

          android:gravity="center_horizontal"

          android:textColor="@color/colorAccent"/>

      <LinearLayout

          android:layout_width="match_parent"

          android:layout_height="wrap_content"

          android:orientation="vertical"

          android:layout_gravity="center"

          android:layout_marginTop="20sp"

          >

          <ImageView

              android:id="@+id/search_image"

              android:layout_width="250dp"

              android:layout_height="250dp"

              android:layout_gravity="center"

              android:maxWidth="250dp"

              android:maxHeight="250dp"

              android:adjustViewBounds="true"

              android:src="@drawable/image_border"

              android:onClick="onClick"/>

          <Button

              android:id="@+id/btn_search_photo"

              android:layout_width="250dp"

              android:layout_height="40dp"

              android:layout_gravity="center"

              android:background="@color/colorPrimary"

              android:text="선택"

              android:textColor="@color/colorWhite"

              android:onClick="onClick"

              android:textSize="15sp"/>

      </LinearLayout>

      <LinearLayout

          android:layout_width="wrap_content"

          android:layout_height="wrap_content"

          android:layout_gravity="center">


      </LinearLayout>


      <Button

          android:layout_width="285dp"

          android:layout_height="40dp"

          android:layout_gravity="center"

          android:layout_marginTop="10dp"

          android:id="@+id/btn_search_next"

          android:text="검색"

          android:textColor="@color/colorAccent"

          android:background="@color/colorPrimaryDark"

          android:onClick="onClick"

          android:textSize="18sp"/>

  </LinearLayout>

</LinearLayout>


manifests 앱 권한 얻기

외부 저장소 읽기 쓰기 권한과 앨범과 카메라를 사용하기 위해 uses-permission 태그를 입력합니다.

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

  package="kookmin.cs.msdj.forstyle">


  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

  <uses-permission android:name="android.permission.CAMERA"/>

  <!-- Samsung -->

  <uses-permission android:name="com.sec.android.provider.badge.permission.READ"/>

  <uses-permission android:name="com.sec.android.provider.badge.permission.WRITE"/>


  <!-- HTC -->

  <uses-permission android:name="com.htc.launcher.permission.READ_SETTINGS"/>

  <uses-permission android:name="com.htc.launcher.permission.UPDATE_SHORTCUT"/>


  <!-- Sony -->

  <uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_BADGE"/>

  <uses-permission android:name="com.sonymobile.home.permission.PROVIDER_INSERT_BADGE"/>


  <!-- Apex -->

  <uses-permission android:name="com.anddoes.launcher.permission.UPDATE_COUNT"/>


  <!-- Solid -->

  <uses-permission android:name="com.majeur.launcher.permission.UPDATE_BADGE"/>


  <!-- Huawei -->

  <uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE" />

  <uses-permission android:name="com.huawei.android.launcher.permission.READ_SETTINGS" />

  <uses-permission android:name="com.huawei.android.launcher.permission.WRITE_SETTINGS" />

  <!-- End: ShortcutBadger -->

  <uses-feature android:name="android.hardware.camera2"/>


  <application

      android:allowBackup="true"

      android:icon="@mipmap/ic_launcher"

      android:label="@string/app_name"

      android:supportsRtl="true"

      android:theme="@style/AppTheme">


      <provider

          android:name="android.support.v4.content.FileProvider"

          android:authorities="${applicationId}.fileprovider"

          android:exported="false"

          android:grantUriPermissions="true">

          <meta-data

              android:name="android.support.FILE_PROVIDER_PATHS"

              android:resource="@xml/provider_paths"/>

      </provider>


      <activity

          android:name=".SplashActivity"

          android:label="@string/title_activity_splash"

          android:theme="@style/AppTheme.NoActionBar">


          <intent-filter>

              <action android:name="android.intent.action.MAIN" />


              <category android:name="android.intent.category.LAUNCHER" />

          </intent-filter>


      </activity>

      <activity

          android:exported="true"

          android:name=".MainActivity"

          android:label="@string/app_name"

          android:theme="@style/AppTheme.NoActionBar">

      </activity>

      <activity

          android:name=".LoginActivity"

          android:label="@string/title_activity_login"

          android:theme="@style/AppTheme.NoActionBar"></activity>

  </application>


</manifest>


SearchImageFragment.java


import android.Manifest;

import android.app.AlertDialog;

import android.app.ProgressDialog;

import android.content.ActivityNotFoundException;

import android.content.DialogInterface;

import android.content.Intent;

import android.content.pm.PackageManager;

import android.database.Cursor;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.net.Uri;

import android.os.Bundle;

import android.os.Environment;

import android.os.Handler;

import android.provider.MediaStore;

import android.support.annotation.NonNull;

import android.support.annotation.Nullable;

import android.support.v4.app.ActivityCompat;

import android.support.v4.app.Fragment;

import android.support.v4.app.FragmentTransaction;

import android.support.v4.content.ContextCompat;

import android.support.v4.content.FileProvider;

import android.util.Log;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.Button;

import android.widget.ImageView;

import android.widget.Toast;


import java.io.BufferedOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.text.SimpleDateFormat;

import java.util.ArrayList;


import kookmin.cs.msdj.forstyle.Server.EC2Server;


import static android.app.Activity.RESULT_OK;


/**

* Created by dblab on 2017-03-07.

*/


public class SearchImageFragment extends Fragment implements View.OnClickListener{


  private EC2Server server;


  // Request Code

  private static final int PICK_FROM_CAMERA = 0;

  private static final int PICK_FROM_ALBUM = 1;

  private static final int CROP_FROM_IMAGE = 2;


  // Fragment component

  private Button btn_search_photo;

  private Button btn_search_next;


  private ImageView iv_search_image;


  private int id_view;

  private Uri mImageCaptureUri;

  private String absoultePath;

  private String strPhotoName;


  private ProgressDialog mProgressDialog;

  private Handler handler;


  public static ArrayList<String> results;


  public Intent CamIntent;

  File file;

  Uri uri;


  @Nullable

  @Override

  public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

      View rootView = inflater.inflate(R.layout.fragment_search_image, container, false);


      /* EC2 Server */

      server = new EC2Server();


      /* Component */

      btn_search_photo = (Button) rootView.findViewById(R.id.btn_search_photo);

      btn_search_next = (Button) rootView.findViewById(R.id.btn_search_next);

      iv_search_image = (ImageView) rootView.findViewById(R.id.search_image);


      results = new ArrayList<String>();

      grantUriPermission();

      /* Click Listener */

      btn_search_photo.setOnClickListener(this);

      btn_search_next.setOnClickListener(this);


      return rootView;

  }


  public void grantUriPermission(){

      // 갤러리 사용 권한 체크

      if(ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {

          // 최초 권한 요청인지 혹은 사용자에게 의한 재요청인지 확인

          if(ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

              // 사용자가 임의로 권한을 취소 시킨 경우, 권한 재요청

              ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);

          } else {

              ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);

          }

      }else if(ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {

          // 최초 권한 요청인지 혹은 사용자에게 의한 재요청인지 확인

          if(ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE)) {

              // 사용자가 임의로 권한을 취소 시킨 경우, 권한 재요청

              ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);

          } else {

              ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);

          }

      }else if(ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

          // 최초 권한 요청인지 혹은 사용자에게 의한 재요청인지 확인

          if(ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.CAMERA)) {

              // 사용자가 임의로 권한을 취소 시킨 경우, 권한 재요청

              ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA}, 1);

          } else {

              ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA}, 1);

          }

      }

  }


  private void CameraOpen() {

      CamIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

      // 사진 저장

      SimpleDateFormat timeFormat = new SimpleDateFormat("yyyymmdd_hhmmss");

      String curTime = timeFormat.format(System.currentTimeMillis());

      String filePath =  "forstyle/"+curTime+".jpg";

      file = new File(Environment.getExternalStorageDirectory(), filePath);

      uri = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", file);

  }


  @Override

  public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

      super.onRequestPermissionsResult(requestCode, permissions, grantResults);

      switch(requestCode) {

          case 1: {

              if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                  return;

              }else {

                  Toast.makeText(getActivity(), "권한 사용을 동의해주셔야 이용 가능합니다.", Toast.LENGTH_LONG).show();

              }

              return;

          }

      }

  }



  @Override

  public void onClick(View v) {

      id_view = v.getId();

      if(id_view == R.id.btn_search_photo) {


          DialogInterface.OnClickListener cameraListener = new DialogInterface.OnClickListener() {

              @Override

              public void onClick(DialogInterface dialog, int which) {

                  doTakePhotoAction();

              }

          };

          DialogInterface.OnClickListener albumListener = new DialogInterface.OnClickListener() {

              @Override

              public void onClick(DialogInterface dialog, int which) {

                  doTakeAlbumAction();

              }

          };


          DialogInterface.OnClickListener cancelListener = new DialogInterface.OnClickListener() {

              @Override

              public void onClick(DialogInterface dialog, int which) {

                  dialog.dismiss();

              }

          };


          new AlertDialog.Builder(getActivity())

                  .setTitle("업로드할 이미지 선택")

                  .setPositiveButton("사진촬영", cameraListener)

                  .setNeutralButton("앨범선택", albumListener)

                  .setNegativeButton("취소", cancelListener)

                  .show();

      }else if(id_view == R.id.btn_search_next) {

          Log.d("SearchImageFragment", "uri "+mImageCaptureUri.getPath().toString());

          Log.d("SearchImageFragment", "absolute path "+absoultePath);

          mProgressDialog = new ProgressDialog(getActivity());

          mProgressDialog.setMessage(getString(R.string.loading));

          mProgressDialog.setIndeterminate(true);

          mProgressDialog.show();

          results = server.uploadPhoto("kjc",mImageCaptureUri, absoultePath);

          mProgressDialog.dismiss();

          Log.d("SearchImageFragment",(results.get(0)));

          int size = results.size();

          String label;

          String[] product_list;

          if(size != 0) {

              String product = results.get(size - 2).toString();

              product = product.replaceAll("\\[", "");

              product = product.replaceAll("\\]", "");

              product = product.replaceAll(" ", "");

              String[] str = product.split(":");

              label = str[0];

              Log.d("SearchImageFragment", product);

              Log.d("SearchImageFragment", "product label : " + label);

              product_list = str[1].split("/");


              //Log.d("SearchImageFragment","product list : "+product_list[0]);

          }else {

              label = "";

              String s = " / / / /";

              product_list = s.split("/");

          }


          ResultShowProductListFragment fragment = new ResultShowProductListFragment();


          Bundle bundle = new Bundle();

          bundle.putString("label", label);

          bundle.putStringArray("product_list", product_list);

          fragment.setArguments(bundle);


          FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();

          ft.add(R.id.content_fragment_layout, fragment);

          ft.replace(R.id.content_fragment_layout, fragment);

          ft.commit();

      }

  }


  /**

   * 카메라에서 사진 촬영

   */

  public void doTakePhotoAction() // 카메라 촬영 후 이미지 가져오기

  {

      Log.d("SearchImageFragment","doTakePhotoAction");

      Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);


      // 임시로 사용할 파일의 경로를 생성

      String url = "tmp_" + String.valueOf(System.currentTimeMillis()) + ".jpg";


      File image;

      try {

          image = File.createTempFile(url, ".jpg", new File(Environment.getExternalStorageDirectory(),"camera.jpg"));

          mImageCaptureUri = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider",image);


      }catch(IOException e) {

          e.printStackTrace();

      }


      mImageCaptureUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), url));

      intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageCaptureUri);

      startActivityForResult(intent, PICK_FROM_CAMERA);


  }


  /**

   * 앨범에서 이미지 가져오기

   */

  public void doTakeAlbumAction() // 앨범에서 이미지 가져오기

  {

      grantUriPermission();

      // 앨범 호출

      Log.d("SearchImageFragment","doTakeAlbumAction");

      Intent intent = new Intent(Intent.ACTION_PICK);

      intent.setType(MediaStore.Images.Media.CONTENT_TYPE);

      startActivityForResult(intent, PICK_FROM_ALBUM);

  }


  @Override

  public void onActivityResult(int requestCode, int resultCode, Intent data) {

      super.onActivityResult(requestCode, resultCode, data);

      Log.d("SearchImageFragment","onActivityResult");

      if (resultCode != RESULT_OK)

          return;

      Log.d("SearchImageFragment","requestCode");

      switch (requestCode) {


          case PICK_FROM_ALBUM: {

              // 이후의 처리가 카메라와 같으므로 일단  break없이 진행합니다.

              // 실제 코드에서는 좀더 합리적인 방법을 선택하시기 바랍니다.

              mImageCaptureUri = data.getData();


              File original_file = getImageFile(mImageCaptureUri);

              mImageCaptureUri = createSaveCropFile();


              File copy_file = new File(mImageCaptureUri.getPath());

              Log.d("SearchImageFragment", mImageCaptureUri.getPath().toString());

              copyCropFile(original_file, copy_file);


              //Intent intent = new Intent("com.android.camera.action.CROP");

              //intent.setDataAndType(mImageCaptureUri, "image/*");

              //intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageCaptureUri);


          }


          case PICK_FROM_CAMERA: {

              //CameraOpen();


              // 이미지를 가져온 이후의 리사이즈할 이미지 크기를 결정합니다.

              // 이후에 이미지 크롭 어플리케이션을 호출하게 됩니다.

              try {

                  Intent intent = new Intent("com.android.camera.action.CROP");

                  intent.setDataAndType(mImageCaptureUri, "image/*");

                  intent.putExtra("crop", "true");

                  intent.putExtra("outputX",120);

                  intent.putExtra("outputY",120);

                  //intent.putExtra("aspectX",3);

                  //intent.putExtra("aspectY",4);

                  //intent.putExtra("scaleUpIfNeeded",true);

                  intent.putExtra("output", mImageCaptureUri);

                  intent.putExtra("return-data", true);

                  startActivityForResult(intent, CROP_FROM_IMAGE); // CROP_FROM_CAMERA case문 이동

              }catch(ActivityNotFoundException ex) {

                  ex.printStackTrace();

              }

              break;

          }

          case CROP_FROM_IMAGE: {

              // 크롭이 된 이후의 이미지를 넘겨 받습니다.

              // 이미지뷰에 이미지를 보여준다거나 부가적인 작업 이후에

              // 임시 파일을 삭제합니다.

              if (resultCode != getActivity().RESULT_OK) {

                  return;

              }


              String img_path = mImageCaptureUri.getPath();

              Bitmap bmp = BitmapFactory.decodeFile(img_path);

              iv_search_image.setImageBitmap(bmp);


              // 사진 저장

              SimpleDateFormat timeFormat = new SimpleDateFormat("yyyymmdd_hhmmss");

              String curTime = timeFormat.format(System.currentTimeMillis());

              String filePath = Environment.getExternalStorageDirectory().getAbsolutePath()+

                      "/forstyle/"+curTime+".jpg";


              //filePath = Environment.getRootDirectory().getAbsolutePath()+"/ForStyle/"+curTime+".jpg";


              // 주석 해제

              storeCropImage(bmp, filePath);

              absoultePath = filePath;


              /*

              if (extras != null) {

                  Bitmap photo = extras.getParcelable("data"); // CROP된 BITMAP

                  // Bitmap.createScaledBitmap() 메소드를 통해 비트맵 이미지를 64*64로 리사이즈한다.

                  //  64*64는 하드웨어 길이에 적용한 사이즈이다.

                  //Bitmap resize_photo = Bitmap.createScaledBitmap(photo, 64, 64, true);


                  iv_search_image.setImageBitmap(photo); // 레이아웃의 이미지칸에 CROP된 BITMAP을 보여줌


                  storeCropImage(photo, filePath); // CROP된 이미지를 외부저장소, 앨범에 저장한다.



                  break;


              }*/


              // 임시 파일 삭제

              /*

              File f = new File(mImageCaptureUri.getPath());

              if (f.exists()) {

                  f.delete();

              }

              */

          }

      }

  }

  /*

   * 외부 저장소가 현재 read와 write를 할 수 잇는 상태인지 확인한다.

   */

  public boolean isExternalStorageWritable() {

      String state = Environment.getExternalStorageState();

      Log.d("SearchImageFragment","isExternalStorageEmulated "+ state);

      if(Environment.MEDIA_MOUNTED.equals(state))

          return true;

      else

          return false;

  }

  /*

   * Bitmap을 저장하는 부분

   */

  private void storeCropImage(Bitmap bitmap, String filePath) {

      boolean isExtenralStorage = isExternalStorageWritable();

      Log.d("SearchImageFragment","isExternalStorageEmulated result : "+ isExtenralStorage);


      // ForStyel 폴더를 생성하여 이미지를 저장하는 방식이다.

      String dirPath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/ForStyle";

      File directory_ForStyle = new File(dirPath);


      String infilePath = getContext().getFilesDir().getAbsolutePath()+"/ForStyle/";

      File in_directory_ForStyle = new File(infilePath);


      if(!directory_ForStyle.exists()) // SmartWheel 디렉터리에 폴더가 없다면 (새로 이미지를 저장할 경우에 속한다.)

          directory_ForStyle.mkdir();

      /*

      if(!directory_system_ForStyle.exists()) // SmartWheel 디렉터리에 폴더가 없다면 (새로 이미지를 저장할 경우에 속한다.)

          directory_system_ForStyle.mkdir();

      */

      File copyFile = new File(filePath);

      BufferedOutputStream out = null;


      try {

          copyFile.createNewFile();

          out = new BufferedOutputStream(new FileOutputStream(copyFile));

          bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);

          Uri photoURI = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", copyFile);

          // sendBroadcast를 통해 Crop된 사진을 앨범에 보이도록 갱신한다.

          getActivity().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, photoURI));

                  //Uri.fromFile(copyFile)));


          out.flush();

          out.close();

      } catch (Exception e) {

          e.printStackTrace();

      }

     

  }


  /**

   * @author : pppdw

   * @param : Crop된 이미지가 저장될 파일을 만든다.선언된 url로 파일이 네이밍되며, 선언된 uri에 파일이 저장된다.

   * @return : Uri

   */

  private Uri createSaveCropFile(){

      Uri uri;

      SimpleDateFormat timeFormat = new SimpleDateFormat("yyyymmdd_hhmmss");

      String curTime = timeFormat.format(System.currentTimeMillis());

      String url = curTime+".jpg";

      //String url = "" + String.valueOf(System.currentTimeMillis()) + ".jpg";

      strPhotoName = url;


      // ForStyle 폴더를 생성하여 이미지를 저장하는 방식이다.

      String dirPath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/forstyle";

      File directory_ForStyle = new File(dirPath);


      if(!directory_ForStyle.exists()) // SmartWheel 디렉터리에 폴더가 없다면 (새로 이미지를 저장할 경우에 속한다.)

          directory_ForStyle.mkdir();


      uri = Uri.fromFile(new File(dirPath, url));

      return uri;

  }


  private File getImageFile(Uri uri) {

      String[] projection = { MediaStore.Images.Media.DATA };

      if (uri == null) {

          uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

      }

      grantUriPermission();

      Cursor mCursor = getActivity().getContentResolver().query(uri, projection, null, null,

              MediaStore.Images.Media.DATE_MODIFIED + " desc");

      if(mCursor == null || mCursor.getCount() < 1) {

          return null; // no cursor or no record

      }

      int column_index = mCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);

      mCursor.moveToFirst();


      String path = mCursor.getString(column_index);


      if (mCursor !=null ) {

          mCursor.close();

          mCursor = null;

      }


      return new File(path);

  }


  /**

   * @author pppdw

   * @description 크롭을 위해 사진을 복사한다.

   * @return

   */


  public static boolean copyCropFile(File srcFile, File destFile) {

      boolean result = false;

      try {

          InputStream in = new FileInputStream(srcFile);

          try {

              result = copyToFile(in, destFile);

          } finally {

              in.close();

          }

      } catch (IOException e) {

          result = false;

      }

      return result;

  }


  /**

   * @author : pppdw

   * @description : DestFile을 소스스트림에 복사한다 (데이터밸류)

   */

  private static boolean copyToFile(InputStream inputStream, File destFile) {

      try {

          OutputStream out = new FileOutputStream(destFile);

          try {

              byte[] buffer = new byte[4096];

              int bytesRead;

              while ((bytesRead = inputStream.read(buffer)) >= 0) {

                  out.write(buffer, 0, bytesRead);

              }

          } finally {

              out.close();

          }

          return true;

      } catch (IOException e) {

          return false;

      }

  }

  // 프로그레스바를 설정한다.

  private void showProgressDialog() {

      if (mProgressDialog == null) {

          mProgressDialog = new ProgressDialog(getActivity());

          mProgressDialog.setMessage(getString(R.string.loading));

          mProgressDialog.setIndeterminate(true);

      }

      mProgressDialog.show();

  }


  // 프로그레스바를 숨긴다.

  private void hideProgressDialog() {

      if (mProgressDialog != null && mProgressDialog.isShowing()) {

          mProgressDialog.hide();

      }

  }

}





코드 앞 부분에 private EC2Server server 부분이 있습니다.


EC2Server는 Amazon EC2 서버로 이미지를 전송하는 코드입니다.

AWS EC2 인스턴스를 받아 웹서버를 설정하고 다시 설명을 진행하겠습니다.





Comments