마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

툴바에필터를추가하고싶습니다.

0 추천

안녕하세요 사진필터 프로그램을 만들고 있는 사람입니다.  두 오픈소스를 결합하여 프로그램을 만들고자 합니다.

인공지능 필터 툴바에는 라이브러리를 활용하여 필터를 넣는 소스코드를 찾았습니다.      https://www.androidhive.info/2017/11/android-building-image-filters-like-instagram/

https://github.com/ravi8x/AndroidPhotoFilters

그리고 명화필터에 tensolflow를 활용한 명화를 명화필터에 넣으려고 하는데 도무지 감이 잡히질 않습니다. ㅠㅠㅠ ( 명화필터에 있는 그림은 합성한것입니다.)   https://github.com/cruzsoma/Fast_Style_Transfer_App

글 작성이 최대 8000byte밖에 안되서 코드를 지운것이 많습니다.

 

이건 라이브러리를 활용하는것이 아닌거 같습니다. 도와주십시오..

package cruzsoma.ai.cnn.fast_style_transfer_app;
public class CameraActivity extends AppCompatActivity {
TensorFlowInferenceInterface tensorFlowInferenceInterface;

    private static final int INPUT_IMAGE_SIZE = 256;
    private static final int INPUT_IMAGE_SIZE_WIDTH = 256;
    private static final int INPUT_IMAGE_SIZE_HEIGHT = 256;
    private static final String INPUT_NAME = "input";
    private static final String OUTPUT_NAME = "output_new";

    private int[] imageIntValues;
    private float[] imageFloatValues;

    private int[] resIds = {R.drawable.starry, R.drawable.ink, R.drawable.mosaic, R.drawable.udnie, R.drawable.wave, R.drawable.cubist, R.drawable.feathers};

    private String[] modelNameList = {"starry", "ink", "mosaic", "udnie", "wave", "cubist", "feathers"};
  
  // 필터 배열로 나열 하는거 같음
    protected ArrayList<Model> initModelsConfig() {
        ArrayList<Model> models = new ArrayList<>();
        for (int i = 0; i < modelNameList.length; i++) {
            Model model = new Model();
            model.type = i;
            model.iconRes = resIds[i];
            model.modelName = modelNameList[i];
            models.add(model);
        }
        return models;
    }
        imageIntValues = new int[INPUT_IMAGE_SIZE_WIDTH * INPUT_IMAGE_SIZE_HEIGHT];
        imageFloatValues = new float[INPUT_IMAGE_SIZE_WIDTH * INPUT_IMAGE_SIZE_HEIGHT * 3];

        ArrayList<Model> modelsConfig = initModelsConfig();
        StyleButtonAdapter styleButtonAdapter = new StyleButtonAdapter(CameraActivity.this, modelsConfig);
        recyclerView.setAdapter(styleButtonAdapter);

        cameraKitView.onStart();

        facingButton.setOnTouchListener(facingButtonTouchListener);
        flashButton.setOnTouchListener(flashButtonTouchListener);
        captureButton.setOnTouchListener(captureButtonTouchListener);

        styleSplitorLayout.setOnTouchListener(styleSplitorTouchListener);
        styleSplitButton.setOnTouchListener(styleSpiltButtonTouchListener);
        refreshButton.setOnTouchListener(refreshButtonTouchListener);

        styleButtonAdapter.buttonSetOnclick(new StyleButtonAdapter.ButtonInterface() {
            @Override
            public void onclick(View view, Model model) {
                Toast toast = Toast.makeText(CameraActivity.this, "이 필터 골랐네요:" + model.modelName, Toast.LENGTH_SHORT);
                toast.setGravity(Gravity.BOTTOM, 0, 350);
                toast.show();
                selectedModel = model.modelName;
            }
        });
    }

    private void cropPictureLeft() {
        float x = styleSplitorLayout.getX() + styleSplitor.getX();
        float rate = x / imageOverlayLayout.getWidth();
        int imageWidth = captureImageBitMap.getWidth();
        int imageHeight = captureImageBitMap.getHeight();
        int width = (int) (imageWidth * rate);
        if (width <= 0) {
            pictureLeft.setLayoutParams(new RelativeLayout.LayoutParams(0, pictureLeft.getHeight()));
        } else {
            Bitmap bitmap = Bitmap.createBitmap(captureImageBitMap, 0, 0, width, imageHeight);
            pictureLeft.setLayoutParams(new RelativeLayout.LayoutParams((int) x, pictureLeft.getHeight()));
            pictureLeft.setImageBitmap(bitmap);
        }
    }

    private View.OnTouchListener styleSplitorTouchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
            switch (motionEvent.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    params.leftMargin = params.leftMargin + (int) motionEvent.getX();
                    view.setLayoutParams(params);
                    cropPictureLeft();
                    break;

                case MotionEvent.ACTION_UP:
                    params.leftMargin = params.leftMargin + (int) motionEvent.getX();
                    view.setLayoutParams(params);
                    break;
            }
            return true;
        }
    };
    void changeViewImageResource(final ImageView imageView, @DrawableRes final int resId) {
        imageView.setRotation(0);
        imageView.animate()
                .rotationBy(360)
                .setDuration(400)
                .setInterpolator(new OvershootInterpolator())
                .start();

        imageView.postDelayed(new Runnable() {
            @Override
            public void run() {
                imageView.setImageResource(resId);
            }
        }, 120);
    }

    boolean handleViewTouchFeedback(View view, MotionEvent motionEvent) {
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                touchDownAnimation(view);
                return true;
            }

            case MotionEvent.ACTION_UP: {
                touchUpAnimation(view);
                return true;
            }

            default: {
                return true;
            }
        }
    }
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        Matrix matrix = new Matrix();
        matrix.postScale(((float) width) / inputWidth, ((float) height) / inputHeight);
        Bitmap scaledBitmap = Bitmap.createBitmap(input, 0, 0, inputWidth, inputHeight, matrix, false);
//        if (!input.isRecycled()) {
//            input.recycle();
//        }
        return scaledBitmap;
    }

    private Bitmap imageStyleTransfer(Bitmap bitmap) {
        Bitmap scaledBitmap = scaleBitmap(bitmap, INPUT_IMAGE_SIZE_WIDTH, INPUT_IMAGE_SIZE_HEIGHT);
        scaledBitmap.getPixels(imageIntValues, 0, scaledBitmap.getWidth(), 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight());

        for (int i = 0; i < imageIntValues.length; ++i) {
            final int val = imageIntValues[i];
            imageFloatValues[i * 3 + 0] = ((val >> 16) & 0xFF) * 1.0f;
            imageFloatValues[i * 3 + 1] = ((val >> 8) & 0xFF) * 1.0f;
            imageFloatValues[i * 3 + 2] = (val & 0xFF) * 1.0f;
        }

        Trace.beginSection("feed");
        tensorFlowInferenceInterface.feed(INPUT_NAME, imageFloatValues, INPUT_IMAGE_SIZE_WIDTH, INPUT_IMAGE_SIZE_HEIGHT, 3);
        Trace.endSection();

        Trace.beginSection("run");
        tensorFlowInferenceInterface.run(new String[]{OUTPUT_NAME});
        Trace.endSection();

        Trace.beginSection("fetch");
        tensorFlowInferenceInterface.fetch(OUTPUT_NAME, imageFloatValues);
        Trace.endSection();

        for (int i = 0; i < imageIntValues.length; ++i) {
            imageIntValues[i] =
                    0xFF000000
                            | (((int) (imageFloatValues[i * 3 + 0])) << 16)
                            | (((int) (imageFloatValues[i * 3 + 1])) << 8)
                            | ((int) (imageFloatValues[i * 3 + 2]));
        }
        scaledBitmap.setPixels(imageIntValues, 0, scaledBitmap.getWidth(), 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight());

        return scaledBitmap;
    }
}

 

최고가되고싶은개발자 (400 포인트) 님이 2021년 2월 4일 질문
최고가되고싶은개발자님이 2021년 2월 4일 수정

1개의 답변

0 추천
첫번째 것은 액티비티 > 프레그먼트 > 리싸이클러뷰 > 아답타

두번째 것은 액티비티 > 리싸이클러뷰 > 아답타입니다.

설명에도 멋지게 나와 있는데, 인스타그램 스타일 필터 오픈 소스는 프레그먼트로 구성했습니다.
그래서 좀 더 복잡하죠. 굳이 프레그먼트로 할 필요가 있을까 싶은데요.
구조적으로는 멋지지만, 데이터가 액티비티와 프레그먼트로 흩어지게 되기 때문에 Readability가 떨어질 가능성이 커요. 한번에 이해가 안가는 부분이 그렇죠.

그러나 OpenCV 나 이미지 핸들링 같은 앱을 만들다보면, 액티비티에는 결과 이미지 뷰만 존재한다고 본다면, 그렇게 복잡하진 않을 수 있고, 다양한 기능 - 자르기, 편집, 색상계 변경, 성형 등등 의 기능을 넣을 때 프레그먼트만 추가하면 되기 때문에, 이런 류의 앱에서는 오히려 좋은 구조라고 생각이 되네요.

두번째 구조는 그냥, AI 필터 기능만 샘플로 구현했기 때문에, 그냥 액티비티 안에 리싸이클러 뷰로 수평 스크롤로만 구현했습니다. 이런 구조는 굉장히 심플하기 때문에, 횡스크롤 리싸이클러뷰 샘플들을 몇개 보기만 해도 금방 이해가 갈 겁니다.

첫번째와 두번째의 차이는 프레그먼트가 있느냐 없느냐의 차이죠.

이것이 그렇게 복잡한 구조가 아니므로, 디버깅 하면서, 다양한 유사 샘플을 보면서
100% 이해하는 게 중요합니다. 100% 이해하는데 1~2일이면 충분한데,
충분히 이해하지 못하고 코드를 짜면, 나중에 1~2주 고생할 수 있습니다.

Good Luck 2 U~!
Will Kim (43,170 포인트) 님이 2021년 2월 5일 답변
필터들을 스위칭하는 하단은 탭바 같은 거로 처리하거나
버튼으로 만들어서
그 위의 프레그먼트를 스위칭하거나
아니면, 리싸이클러뷰의 데이터만 스위칭하면 될 것으로 보이네요.
...