Полное растровое изображение не заполняется данными пикселей после распараллеливания. - PullRequest
0 голосов
/ 13 ноября 2011

У меня есть приложение, которое обрабатывает растровое изображение со сферическим искажением.Вы можете коснуться экрана и установить радиус круга, который будет содержать искажения.После нажатия кнопки искажения создается битовая карта поднабора того же размера радиуса, и эта битовая карта поднабора отправляется на обработку.Как только подмножество искажено, оно возвращается на исходное растровое изображение в качестве наложения, используя шнуры x, y из исходного события касания.

Все работает отлично, за исключением того, что последняя строка пикселей (в нижней части) растрового изображения подмножества не заполнена данными пикселей.Похоже, что есть черная линия в нижней части растрового изображения подмножества.Класс искажения использует параллельное программирование.Это проверяет оборудование во время выполнения, чтобы узнать, сколько процессоров доступно, и разбивает растровое изображение на процессор соответственно.Я помог с распараллеливанием и не уверен, как узнать, почему присутствует черная линия.Циклы вроде бы в порядке, есть идеи?Заранее спасибо Мэтт.

.

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

import android.graphics.Bitmap;
import android.os.Debug;
import android.util.Log;

public class MultiRuntimeProcessorFilter {



    private static final String TAG = "mrpf";
    private int x = 0;
    private Bitmap input = null;
    private int radius;


    public void createBitmapSections(int nOp, int[] sections){

        int processors = nOp;
        int jMax = input.getHeight();
        int aSectionSize = (int) Math.ceil(jMax/processors);
        Log.e(TAG, "++++++++++ sections size = "+aSectionSize);


        int k = 0;
        for(int h=0; h<processors+1; h++){

                sections[h] = k;
                k+= aSectionSize;



        }
    }// end of createBitmapSections()



    @SuppressWarnings("unchecked")
    public Bitmap barrel (Bitmap input, float k, int r){
          this.radius = r;
          this.input = input;
          int []arr = new int[input.getWidth()*input.getHeight()];




          int nrOfProcessors = Runtime.getRuntime().availableProcessors();
          int[] sections = new int[nrOfProcessors+1];


          createBitmapSections(nrOfProcessors,sections);
          ExecutorService threadPool = Executors.newFixedThreadPool(nrOfProcessors);

          for(int g=0; g<sections.length;g++){
              Log.e(TAG, "++++++++++ sections= "+sections[g]);
          }

         // ExecutorService threadPool = Executors.newFixedThreadPool(nrOfProcessors);

          Object[] task = new Object[nrOfProcessors];

          for(int z = 0; z < nrOfProcessors; z++){
             task[z]  = (FutureTask<PartialResult>) threadPool.submit(new PartialProcessing(sections[z], sections[z+1] - 1, input, k));  
             Log.e(TAG, "++++++++++ task"+z+"= "+task[z].toString()); 
          }

         PartialResult[] results = new PartialResult[nrOfProcessors];

         try{
              for(int t = 0; t < nrOfProcessors; t++){

                  results[t] = ((FutureTask<PartialResult>) task[t]).get();

                  results[t].fill(arr);
              }

          }catch(Exception e){
              e.printStackTrace();
          }

          Bitmap dst2 = Bitmap.createBitmap(arr,input.getWidth(),input.getHeight(),input.getConfig());


        return dst2;


        }//end of barrel()




    public class PartialResult {
           int startP;
           int endP;
           int[] storedValues;

           public PartialResult(int startp, int endp, Bitmap input){

               this.startP = startp;
               this.endP = endp;
               this.storedValues = new int[input.getWidth()*input.getHeight()];


           }

           public void addValue(int p, int result) {
                 storedValues[p] = result;

           }

           public void fill(int[] arr) {



              for (int p = startP; p < endP; p++){
                  for(int b=0;b<radius;b++,x++)
                 arr[x] = storedValues[x];

              } 
              Log.e(TAG, "++++++++++ x ="+x);
              }

           }//end of partialResult




    public class PartialProcessing implements Callable<PartialResult> {
        int startJ;
        int endJ;


        private int[] scalar;
        private float xscale;
        private float yscale;
        private float xshift;
        private float yshift;
        private float thresh = 1;
        private int [] s1;
        private int [] s2;
        private int [] s3;
        private int [] s4;
        private int [] s;
        private Bitmap input;
        private float k;



        public PartialProcessing(int startj, int endj, Bitmap input, float k) {

            this.startJ = startj;
            this.endJ = endj;
            this.input = input;
            this.k = k;

            s = new int[4];
            scalar = new int[4];
            s1 = new int[4];
            s2 = new int[4];
            s3 = new int[4];
            s4 = new int[4];

        }

        int [] getARGB(Bitmap buf,int x, int y){

            int rgb = buf.getPixel(y, x); // Returns by default ARGB.
            // int [] scalar = new int[4];
           //  scalar[0] = (rgb >>> 24) & 0xFF;
             scalar[1] = (rgb >>> 16) & 0xFF;
             scalar[2] = (rgb >>> 8) & 0xFF;
             scalar[3] = (rgb >>> 0) & 0xFF;
             return scalar;

        }



        float getRadialX(float x,float y,float cx,float cy,float k){

            x = (x*xscale+xshift);
            y = (y*yscale+yshift);
            float res = x+((x-cx)*k*((x-cx)*(x-cx)+(y-cy)*(y-cy)));
            return res;
          }

          float getRadialY(float x,float y,float cx,float cy,float k){

            x = (x*xscale+xshift);
            y = (y*yscale+yshift);
            float res = y+((y-cy)*k*((x-cx)*(x-cx)+(y-cy)*(y-cy)));
            return res;
          }



          float calc_shift(float x1,float x2,float cx,float k){

            float x3 = (float)(x1+(x2-x1)*0.5);
            float res1 = x1+((x1-cx)*k*((x1-cx)*(x1-cx)));
            float res3 = x3+((x3-cx)*k*((x3-cx)*(x3-cx)));

            if(res1>-thresh && res1 < thresh)
              return x1;
            if(res3<0){
              return calc_shift(x3,x2,cx,k);
            }
            else{
              return calc_shift(x1,x3,cx,k);
            }
          }


          void sampleImage(Bitmap arr, float idx0, float idx1)
          {

             // s = new int [4];
            if(idx0<0 || idx1<0 || idx0>(arr.getHeight()-1) || idx1>(arr.getWidth()-1)){
              s[0]=0;
              s[1]=0;
              s[2]=0;
              s[3]=0;
              return;
            }

            float idx0_fl=(float) Math.floor(idx0);
            float idx0_cl=(float) Math.ceil(idx0);
            float idx1_fl=(float) Math.floor(idx1);
            float idx1_cl=(float) Math.ceil(idx1);



             s1 = getARGB(arr,(int)idx0_fl,(int)idx1_fl);
             s2 = getARGB(arr,(int)idx0_fl,(int)idx1_cl);
             s3 = getARGB(arr,(int)idx0_cl,(int)idx1_cl);
             s4 = getARGB(arr,(int)idx0_cl,(int)idx1_fl);

            float x = idx0 - idx0_fl;
            float y = idx1 - idx1_fl;

           // s[0]= (int) (s1[0]*(1-x)*(1-y) + s2[0]*(1-x)*y + s3[0]*x*y + s4[0]*x*(1-y));
            s[1]= (int) (s1[1]*(1-x)*(1-y) + s2[1]*(1-x)*y + s3[1]*x*y + s4[1]*x*(1-y));
            s[2]= (int) (s1[2]*(1-x)*(1-y) + s2[2]*(1-x)*y + s3[2]*x*y + s4[2]*x*(1-y));
            s[3]= (int) (s1[3]*(1-x)*(1-y) + s2[3]*(1-x)*y + s3[3]*x*y + s4[3]*x*(1-y));


          }



        @Override public PartialResult call() { 

             PartialResult partialResult = new PartialResult(startJ, endJ,input);

             float centerX=input.getWidth()/2; //center of distortion
             float centerY=input.getHeight()/2;



             int width = input.getWidth(); //image bounds
             int height = input.getHeight();



              xshift = calc_shift(0,centerX-1,centerX,k);

              float newcenterX = width-centerX;
              float xshift_2 = calc_shift(0,newcenterX-1,newcenterX,k);

              yshift = calc_shift(0,centerY-1,centerY,k);

              float newcenterY = height-centerY;
              float yshift_2 = calc_shift(0,newcenterY-1,newcenterY,k);

              xscale = (width-xshift-xshift_2)/width;

              yscale = (height-yshift-yshift_2)/height;


            int p = startJ*radius; 
            int origPixel = 0;
            int color = 0;
            int i;

            for (int j = startJ; j <  endJ; j++){

                for ( i = 0; i < width; i++, p++){


             origPixel = input.getPixel(i,j);

             float x = getRadialX((float)j,(float)i,centerX,centerY,k);


             float y = getRadialY((float)j,(float)i,centerX,centerY,k);

             sampleImage(input,x,y);

             color = ((s[1]&0x0ff)<<16)|((s[2]&0x0ff)<<8)|(s[3]&0x0ff);
            //Log.e(TAG, "radius = "+radius);

             if(((i-centerX)*(i-centerX) + (j-centerY)*(j-centerY)) <= radius*(radius/4)){

                                 partialResult.addValue(p, color);


            }else{


                partialResult.addValue(p, origPixel);



            }

                }//end of inner for

        }//end of outer for

            return partialResult;
    }//end of call


}// end of partialprocessing

}//end of MultiProcesorFilter

.

[обновить] Я опубликую класс представления, который вызывает метод барреля.этот класс получает события касания и устанавливает радиус искажения до обработки.Вы можете увидеть, как все настраивается до применения искажения.

public class TouchView extends View{


    private File tempFile;
    private byte[] imageArray;
    private Bitmap bgr;

    private Bitmap crop;
    private Bitmap crop2;
    private Bitmap overLay;
    private Bitmap overLay2;

    private Paint pTouch;
    private float centreX;
    private float centreY;
    private float centreA;
    private float centreB;
    private Boolean xyFound = false;
    private Boolean abFound = false;
    private int Progress = 1;
    private static final String TAG = "*********TouchView";
    private Filters f = null;
    private Filters f2 = null;
    private boolean bothCirclesInPlace = false;
    private MultiProcessorFilter mpf;
    private MultiProcessorFilter mpf2;
    private MultiRuntimeProcessorFilter mrpf;
    private MultiRuntimeProcessorFilter mrpf2;
    private  int radius = 50;
    protected boolean isLocked = false;
    protected boolean isSaved = false;
    protected byte [] data;
    private float distance1;
    private float distance2;



    public TouchView(Context context) {
        super(context);

    }




    public TouchView(Context context, AttributeSet attr) {
        super(context,attr);
        Log.e(TAG, "++++++++++ inside touchview constructor");



        tempFile = new File(Environment.getExternalStorageDirectory().
                getAbsolutePath() + "/"+"image.jpeg");

        imageArray = new byte[(int)tempFile.length()];


    // new Thread(new Runnable() {
    //      public void run() {



     try{

            InputStream is = new FileInputStream(tempFile);
            BufferedInputStream bis = new BufferedInputStream(is);
            DataInputStream dis = new DataInputStream(bis);


            int i = 0;

            while (dis.available() > 0 ) {
            imageArray[i] = dis.readByte();
            i++;
            }

            dis.close();


       } catch (Exception e) {

               e.printStackTrace();
            }

         //    }
          //    }).start();

       Bitmap bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length);

        if(bm == null){
            Log.e(TAG, "bm = null");
        }else{
            Log.e(TAG, "bm =  not null");
        }


        bgr = bm.copy(bm.getConfig(), true);

        overLay = null;
        overLay2 = null;



       bm.recycle();

    pTouch = new Paint(Paint.ANTI_ALIAS_FLAG);         
   // pTouch.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT)); 
    pTouch.setColor(Color.RED);
    pTouch.setStyle(Paint.Style.STROKE);


    }// end of touchView constructor


    public void findCirclePixels(){ 


              //f = new Filters();
              // f2 = new Filters();
              //mpf = new MultiProcessorFilter();
              //mpf2 = new MultiProcessorFilter();
            mrpf = new MultiRuntimeProcessorFilter();
            mrpf2 = new MultiRuntimeProcessorFilter();
            crop = Bitmap.createBitmap(bgr,Math.max((int)centreX-radius,0),Math.max((int)centreY-radius,0),radius*2,radius*2);
            crop2 = Bitmap.createBitmap(bgr,Math.max((int)centreA-radius,0),Math.max((int)centreB-radius,0),radius*2,radius*2);

              new Thread(new Runnable() {
                public void run() {
                    float prog = (float)Progress/150001;

              // final Bitmap bgr3 = f.barrel(crop,prog);
              //  final Bitmap bgr4 = f2.barrel(crop2,prog);

                //final Bitmap bgr3 = mpf.barrel(crop,prog);
               // final Bitmap bgr4 = mpf2.barrel(crop2,prog);

                    final Bitmap bgr3 = mrpf.barrel(crop,prog,radius*2);
                    final Bitmap bgr4 = mrpf2.barrel(crop2,prog, radius*2);

                  TouchView.this.post(new Runnable() {
                    public void run() {


                      TouchView.this.overLay = bgr3;
                      TouchView.this.overLay2 = bgr4;

                      TouchView.this.invalidate();
                    }
                  });
                }
              }).start();




        }// end of findCirclePixels()




    @Override
    public boolean onTouchEvent(MotionEvent ev) {



        switch (ev.getAction()) {



            case MotionEvent.ACTION_DOWN: {

                 int w = getResources().getDisplayMetrics().widthPixels;
                 int h = getResources().getDisplayMetrics().heightPixels;

                 if(ev.getX() <radius || ev.getX() > w - radius ){

                    // Log.e(TAG, "touch event is too near width edge!!!!!!!!!!");
                     showToastMessage("You touched too near the screen edge");
                     break;

                 }

                    if(ev.getY() <radius || ev.getY() > h  - radius ){

                    // Log.e(TAG, "touch event is too near height edge!!!!!!!!!!");
                     showToastMessage("You touched too near the screen edge");
                     break;
                 }

                 distance1 = (float) Math.sqrt(Math.pow(ev.getX() - centreX, 2.0) + Math.pow(ev.getY() - centreY, 2.0));
                 distance2 = (float) Math.sqrt(Math.pow(ev.getX() - centreA, 2.0) + Math.pow(ev.getY() - centreB, 2.0));
                 Log.e(TAG, "dist1 = "+distance1 +" distance2 = " + distance2);


                if(isLocked == false){

                if(abFound == false){
                    centreA = (int) ev.getX();
                    centreB = (int) ev.getY();
                    abFound = true;


                    invalidate();

                    }

                if(xyFound == false){
                centreX = (int) ev.getX();
                centreY = (int) ev.getY();
                xyFound = true;
                invalidate();

                }


                if(abFound == true && xyFound == true){
                 bothCirclesInPlace  = true;
                }
                break;
                }
            }

            case MotionEvent.ACTION_MOVE: {

                if(isLocked == false){

                /*if(xyFound == false){
                    centreX = (int) ev.getX()-70;
                    centreY = (int) ev.getY()-70;
                    xyFound = true;
                }else{
                    centreA = (int) ev.getX()-70;
                    centreB = (int) ev.getY()-70;
                    bothCirclesInPlace = true;
                    invalidate();
                    }
                    */


                    if(distance1 < distance2){
                        centreX = (int) ev.getX();
                        centreY = (int) ev.getY();
                        xyFound = true;
                        invalidate();
                    }else{
                        centreA = (int) ev.getX();
                        centreB = (int) ev.getY();
                        bothCirclesInPlace = true;
                        invalidate();
                        }
                    break;
                }

            }          

            case MotionEvent.ACTION_UP: 


                break;

        }

        return true;

    }//end of onTouchEvent





    public void initSlider(final HorizontalSlider slider)
    {

        slider.setOnProgressChangeListener(changeListener);


    }



    private OnProgressChangeListener changeListener = new OnProgressChangeListener() {


        @Override
        public void onProgressChanged(View v, int progress) {


            if(isLocked == true){
              setProgress(progress);
            }else{
                Toast.makeText(TouchView.this.getContext(), "press lock before applying distortion ", Toast.LENGTH_SHORT).show();
            }



        }
    };





    @Override
    public void onDraw(Canvas canvas){
        super.onDraw(canvas);



        Log.e(TAG, "******about to draw bgr ");
        canvas.drawBitmap(bgr, 0, 0, null);

        if(isSaved == false){

        if (isLocked == true && bothCirclesInPlace == true){
            if(overLay != null)
             canvas.drawBitmap(overLay, centreX-radius, centreY-radius, null);
            if(overLay2 != null)
             canvas.drawBitmap(overLay2, centreA-radius, centreB-radius, null);
        }


        if(bothCirclesInPlace == true && isLocked == false){


            canvas.drawCircle(centreX, centreY, radius,pTouch);
            canvas.drawCircle(centreA, centreB, radius,pTouch);     

        }    

        }else{

        // String mFilePath  : Absolute Path of the file to be saved 

        // Bitmap mBitmap1   : First bitmap. This goes as background.
        // Bitmap mCBitmap   : Bitmap associated with the Canvas. All draws on the canvas are drawn into this bitmap.
        // Bitmap mBitmap2   : Second bitmap. This goes on top of first (in this example serves as foreground.

        // Paint mPaint1     : Paint to draw first bitmap
        // Paint mPaint2     : Paint to draw second bitmap on top of first bitmap

        isSaved = false;
        Bitmap mCBitmap = Bitmap.createBitmap(bgr.getWidth(), bgr.getHeight(), bgr.getConfig());

        Canvas tCanvas = new Canvas(mCBitmap);

        tCanvas.drawBitmap(bgr, 0, 0, null);



        if(overLay != null)
        tCanvas.drawBitmap(overLay, centreX-radius, centreY-radius, null);

        if(overLay2 != null)
             tCanvas.drawBitmap(overLay2, centreA-radius, centreB-radius, null);

        canvas.drawBitmap(mCBitmap, 0, 0, null);



        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        mCBitmap.compress(CompressFormat.JPEG, 100 /*ignored for PNG*/, bos);
        data = bos.toByteArray();
        try {
            bos.flush();
            bos.close();
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        try {
            bos.flush();
            bos.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        if ( data == null){
            Log.e(TAG, "data in touchview before save clicked is null");
        }else{
            Log.e(TAG, "data in touchview before saved clicked is not null");
        }


        }

    }//end of onDraw




    protected void setProgress(int progress2) {
        Log.e(TAG, "***********in SETPROGRESS");
        this.Progress = progress2;



        findCirclePixels();


    }




    public  int getRadius() {
        return radius;
    }




    public  void setRadius(int r) {
        radius = r;
        invalidate();
    }


    public void showToastMessage(String mess){

        Toast.makeText(TouchView.this.getContext(), mess.toString(), Toast.LENGTH_SHORT).show();

    }

}

Ответы [ 3 ]

0 голосов
/ 18 ноября 2011

просто предположение, что произойдет, если вы поставите это

BitmapDrawable bmpd = new BitmapDrawable(input);
int []arr = new int[(bmpd.getIntrinsicWidth() + this.radius) * (bmpd. getIntrinsicHeight() + this.radius)];
0 голосов
/ 18 ноября 2011

Скорее всего, ваша проблема связана с предполагаемой системой координат изображения и алгоритма сферизации.

См. Системы координат изображений MathWorks

Я ожидаю, что вы обрабатываете свои входные / выходные изображения в соответствии с методом индексов пикселей, но алгоритм сферизации обрабатывает ваши данные с использованием пространственной системы координат. Это часто приводит к тому, что внешняя граница обработанного изображения становится пустой, поскольку алгоритм перевел ваше изображение вверх и влево на 0,5 пикселя. Координата 3 в исходной системе теперь равна 3,5 в новой системе и вышла за пределы вычислений.

Это на самом деле огромная проблема в алгоритмах обработки изображений 2D-3D, поскольку проекция между двумя пространствами не совсем тривиальна, а крошечные различия в реализации вызывают заметные проблемы. Обратите внимание, что система координат «Пиксельные индексы» имеет размер 3x3, но система пространственных координат по существу равна 4x4.

Попробуйте установить для своей бочки сферизации ширину + 1 / высоту + 1 вместо ширины / высоты и посмотрите, не заполняет ли она пропущенный ряд.

0 голосов
/ 18 ноября 2011

Я полагаю, что при обработке нижней части изображения операция выполняется частично над входным изображением и частично за пределами изображения из-за радиуса в методе с бочкой. Края часто могут вызывать проблемы при работе за пределами фактического изображения, давая в результате 0, что может вызвать черную линию ...

Предлагаю попробовать увеличить размер вашего изображения:

@SuppressWarnings("unchecked")
public Bitmap barrel (Bitmap input, float k, int r){
    this.radius = r;
    this.input = input;

    // Add an offset to the width and height equal to the radius
    // To avoid performing processing outside the bounds of the input image
    int []arr = new int[(input.getWidth() + this.radius) * (input.getHeight() + this.radius)];
    // Continue...

Опять же, это мое первое предположение, и у меня нет времени, чтобы проверить прямо сейчас, но, конечно же, моя рекомендация будет первой.

...