[英]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);
}
}
上面的代码执行以下操作:
触摸“视图”后,它会检测触摸是否在图像的脸上。 如果是,则使用invalidate()
在脸上绘制一个裁剪框。
视觉API给出的Rect
使用getFaceRect()
方法进行裁剪。
面部的位图由方法getFaceBitmapWithIndex()
,如果图像中有多个面部,则需要索引。 否则,索引为0。
问题:获取面部的位图时使用的Rect
是getFaceRect()
给出的。 但是,我无法获得正确的区域。 总会有一些偏移。
图片说明了这种情况:
视觉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.