代码之家  ›  专栏  ›  技术社区  ›  Susmit Agrawal Sudip Bolakhe

ImageView位图偏移校正

  •  0
  • Susmit Agrawal Sudip Bolakhe  · 技术社区  · 6 年前

    我正在尝试创建跨多个图像的人脸交换的实现。这些图像不是从相机拍摄的。它们是设备上的 .jpg 文件。

    我需要使用谷歌的人脸检测API提取人脸图像的一部分。为此,我扩展了ImageView:

    public class face提取器imageview extends imageview{
    
    位图图像;
    位图原图;
    
    上下文上下文;
    面部探测器;
    sparsearray<face>面;
    列表<rect>facerects;
    布尔值更改=假;
    
    人脸选择人脸;
    
    公共FaceExtractorImageView(上下文上下文){
    super(上下文);
    this.context=上下文;
    
    detector=new facedetector.builder(上下文)
    .setEmergentFaceOnly(错误)
    .setLandmarkType(人脸检测器。所有标志)
    .build();
    facerects=新数组列表<>();
    
    //其他构造函数执行完全相同的操作。
    }
    
    @覆盖
    公共void setimagebitmap(最终位图img){
    图像=img;
    selectedface=空;
    如果(!)已更改)
    原始图像=img;
    
    更改=真;
    
    super.setImageBitmap(img);
    faces=detector.detect(new frame.builder().setBitmap(image.build());
    
    facerects.clear();
    对于(int i=0;i<faces.size();i++)
    facerects.add(getfacerect(i));
    
    setOntouchlistener(new Ontouchlistener()){
    @覆盖
    公共布尔型OnTouch(视图V,MotionEvent事件){
    selectedface=空;
    开关(event.getAction()){
    case motionevent.action\u向上:
    对于(int i=0;i<facerects.size();i++)
    如果(facerects.get(i){
    。包含((int)event.getx(),
    (int)event.gety())事件{
    selectedFace=面.valueat(i);
    }
    invalidate();
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    public class FaceExtractorImageView extends ImageView {
    
        Bitmap image;
        Bitmap origImage;
    
        Context context;
        FaceDetector detector;
        SparseArray<Face> faces;
        List<Rect> faceRects;
        boolean changed = false;
    
        Face selectedFace;
    
        public FaceExtractorImageView(Context context) {
            super(context);
            this.context = context;
    
            detector = new FaceDetector.Builder(context)
                    .setProminentFaceOnly(false)
                    .setLandmarkType(FaceDetector.ALL_LANDMARKS)
                    .build();
            faceRects = new ArrayList<>();
    
            //The other constructors do the exact same thing.
        }
    
        @Override
        public void setImageBitmap(final Bitmap img){
            image = img;
            selectedFace = null;
            if(!changed)
                origImage = img;
    
            changed =true;
    
            super.setImageBitmap(img);
            faces = detector.detect(new Frame.Builder().setBitmap(image).build());
    
            faceRects.clear();
            for(int i=0;i<faces.size();i++)
                faceRects.add(getFaceRect(i));
    
            setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    selectedFace = null;
                    switch(event.getAction()){
                        case MotionEvent.ACTION_UP:
                            for(int i=0;i<faceRects.size();i++)
                                if(faceRects.get(i) {
                                    .contains((int)event.getX(), 
                                    (int)event.getY())) {
                                   selectedFace = faces.valueAt(i);
                                }
                            invalidate();
                    }
                    return true;
                }
            });
        }
    
        @Override
        public void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if(selectedFace!=null){
                Paint p = new Paint();
                p.setStyle(Paint.Style.STROKE);
                p.setColor(Color.RED);
                canvas.drawRect(getFaceRect(selectedFace), p);
            }
        }
    
        public Face getSelectedFace(){
            return selectedFace;
        }
    
        public Bitmap getSelectedFaceBitmap() {
            if(selectedFace==null)
                return null;
    
            int index = faces.indexOfValue(selectedFace);
            return getFaceBitmapWithIndex(index);
        }
    
        //Returns Bitmap of face with index @index form the currently set image
        public Bitmap getFaceBitmapWithIndex(int index) {
            return getFaceBitmapWithIndex(image, index);
        }
    
    
        //Returns Bitmap of face with index @index form image @src
        public Bitmap getFaceBitmapWithIndex(Bitmap src, int index) {
            Rect r = getFaceRect(index);
            Bitmap created = Bitmap.createBitmap(src, r.left, r.top, r.width(), r.height());
            ImageView v = new ImageView(context);
            v.setImageBitmap(created);
            //new AlertDialog.Builder(context).setView(v).create().show();
            return created;
        }
    
    
        public Rect getFaceRect(Face face){
            Rect r= new Rect();
    
            //The constant values used below are for cropping the original image.
            r.left = (int) face.getPosition().x + (int) face.getWidth() / 20;
    
            r.top = (int) face.getPosition().y + (int) face.getHeight() / 4;
    
            r.right = r.left + (int) (face.getWidth() * 5.75 / 10);
    
            r.bottom = r.top + (int) (face.getHeight() * 3 / 5);
            return r;
        }
    
        //Get rect for face at index @index of image @img
        public Rect getFaceRect(int index){
            Face face = faces.valueAt(index);
            if(face==null) {
                Log.e("Face not found", "The image in the view does not have a face with index "+index);
                return null;
            }
    
            return getFaceRect(face);
        }
    }
    

    1. invalidate()

    2. RectgetFaceRect()

    3. getFaceBitmapWithIndex()

    enter image description here

    onDraw()

    enter image description here

    enter image description here

    1 回复  |  直到 6 年前
        1
  •  0
  •   Susmit Agrawal Sudip Bolakhe    6 年前

    我找到了虫子。事实证明,我没有考虑不同图像的不同尺寸,相对于视图本身。

    所以,我把代码放在 super 在里面 setImageBitmap getViewTreeObserver().addOnGlobalLayoutListener 并建立了一个新的 DrawingCache 那里:

    @Override
    public void setImageBitmap(final Bitmap img){
        if(!changed)
            origImage = img;
    
        changed =true;
    
        super.setImageBitmap(img);
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                buildDrawingCache();
                image = getDrawingCache();
                selectedFace = null;
                faces = detector.detect(new Frame.Builder().setBitmap(image).build());
    
                faceRects.clear();
                for (int i = 0; i < faces.size(); i++)
                    faceRects.add(getFaceRect(i));
    
                setOnTouchListener(new OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        selectedFace = null;
                        switch (event.getAction()) {
                            case MotionEvent.ACTION_UP:
                                for (int i = 0; i < faceRects.size(); i++)
                                    if (faceRects.get(i).contains((int) event.getX(), (int) event.getY())) {
                                        //Toast.makeText(context, "Touched", Toast.LENGTH_LONG).show();
                                        selectedFace = faces.valueAt(i);
                                    }
                                invalidate();
                        }
                        return true;
                    }
                });
            }
        });
    }