繁体   English   中英

ImageView位图偏移校正

[英]ImageView bitmap offset correction

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

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

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. 视觉API给出的Rect使用getFaceRect()方法进行裁剪。

  3. 面部的位图由方法getFaceBitmapWithIndex() ,如果图像中有多个面部,则需要索引。 否则,索引为0。

问题:获取面部的位图时使用的RectgetFaceRect()给出的。 但是,我无法获得正确的区域。 总会有一些偏移。

图片说明了这种情况:

视觉API给出的Rect

在此处输入图片说明

getFaceRect()生成的Rect 当调用invalidate()时,也将在onDraw()绘制该onDraw()

在此处输入图片说明

我在getFaceBitmapWithIndex()时获得的区域称为:

在此处输入图片说明

在这里我缺少一些非常简单的东西,但是我无法弄清楚那是什么。 任何帮助,将不胜感激。

我发现了错误。 事实证明,关于View本身,我没有考虑不同图像的尺寸变化。

因此,我将代码放置在getViewTreeObserver().addOnGlobalLayoutListener内的setImageBitmap super调用之后,并在那里建立了一个新的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;
                }
            });
        }
    });
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM