MATLAB与FIJI统计区域合并算法速度:为什么FIJI更快?

时间:2016-02-04 18:02:33

标签: java image performance matlab imagej

我在MATLAB中组装了一个算法,并且使用统计区域合并作为其中的一部分。我在MATLAB和斐济(ImageJ)中有这个版本,两者都包含在下面。两个版本都基于Nock和Nielson模型,看起来很相似(虽然我对Java /斐济不太熟悉)。当我在MATLAB中使用1500x500 2d灰度图像运行时,处理大约需要3秒钟,而斐济需要不到一秒钟。我尽可能地尽量减少了MATLAB代码,但仍然遇到了这个速度问题。造成这种时间差异的原因是什么?感谢

MATLAB

    % Statistical Region Merging
%
% Nock, Richard and Nielsen, Frank 2004. Statistical Region Merging. IEEE Trans. Pattern Anal. Mach. Intell. 26, 11 (Nov. 2004), 1452-1458.
% DOI= http://dx.doi.org/10.1109/TPAMI.2004.110

%Segmentation parameter Q; Q small few segments, Q large may segments

function [maps,images]=srm3(image,Qlevels)

% Smoothing the image, comment this line if you work on clean or synthetic images
h=fspecial('gaussian',[3 3],1);
image=imfilter(image,h,'symmetric');

smallest_region_allowed=10;

size_image=size(image);
n_pixels=size_image(1)*size_image(2);

% Compute image gradient
[Ix,Iy]=srm_imgGrad(image(:,:,:));
Ix=max(abs(Ix),[],3);
Iy=max(abs(Iy),[],3);
normgradient=sqrt(Ix.^2+Iy.^2);

Ix(:,end)=[];
Iy(end,:)=[];

[~,index]=sort(abs([Iy(:);Ix(:)]));

n_levels=numel(Qlevels);
maps=cell(n_levels,1);
images=cell(n_levels,1);
im_final=zeros(size_image);
Q=256;
map=reshape(1:n_pixels,size_image(1:2));
% gaps=zeros(size(map)); % For future release
treerank=zeros(size_image(1:2));

size_segments=ones(size_image(1:2));
image_seg=image;

%Building pairs
n_pairs=numel(index);
idx2=reshape(map(:,1:end-1),[],1);
idx1=reshape(map(1:end-1,:),[],1);

pairs1=[ idx1;idx2 ];
pairs2=[ idx1+1;idx2+size_image(1) ];


    for i=1:n_pairs
        C1=pairs1(index(i));
        C2=pairs2(index(i));

        %Union-Find structure, here are the finds, average complexity O(1)
        while (map(C1)~=C1 ); C1=map(C1); end
        while (map(C2)~=C2 ); C2=map(C2); end

        % Compute the predicate, region merging test
        g=256;
        logdelta=2*log(6*n_pixels);

        dR=(image_seg(C1)-image_seg(C2))^2;

        logreg1 = min(g,size_segments(C1))*log(1.0+size_segments(C1));
        logreg2 = min(g,size_segments(C2))*log(1.0+size_segments(C2));

        dev1=((g*g)/(2.0*Q*size_segments(C1)))*(logreg1 + logdelta);
        dev2=((g*g)/(2.0*Q*size_segments(C2)))*(logreg2 + logdelta);

        dev=dev1+dev2;

        predicat=( (dR<dev) ); 

        if (((C1~=C2)&&predicat) ||  xor(size_segments(C1)<=smallest_region_allowed, size_segments(C2)<=smallest_region_allowed))
            % Find the new root for both regions
            if treerank(C1) > treerank(C2)
                map(C2) = C1; reg=C1;
            elseif treerank(C1) < treerank(C2)
                map(C1) = C2; reg=C2;
            elseif C1 ~= C2
                map(C2) = C1; reg=C1;
                treerank(C1) = treerank(C1) + 1;
            end

            if C1~=C2
                % Merge region
                nreg=size_segments(C1)+size_segments(C2);
                size_segments(C1);
                size_segments(C2);
                image_seg(reg)=(size_segments(C1)*image_seg(C1)+size_segments(C2)*image_seg(C2))/nreg;
                size_segments(reg)=nreg;
            end
        end
    end

    while 1
        map_ = map(map) ;
        if isequal(map_,map) ; break ; end
        map = map_ ;
    end

    im_final(:,:,1)=image_seg(map+(1-1)*n_pixels);
    images{1}=im_final;

Fiji

 * Statistical Region Merging.
 * %%
 * Copyright (C) 2009 - 2013 Johannes Schindelin.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of any organization.
 * #L%
 */


import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.GenericDialog;
import ij.plugin.filter.PlugInFilter;
import ij.process.ByteProcessor;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import ij.process.ShortProcessor;

import java.util.Arrays;

/*
 * The Statistical Region Merging algorithm is described in
 *
 * R. Nock, F. Nielsen: Statistical Region Merging.
 * IEEE Trans. Pattern Anal. Mach. Intell. 26(11): 1452-1458 (2004)
 */

public class SRM_ implements PlugInFilter {
    ImagePlus image;

    public int setup(String arg, ImagePlus image) {
        this.image = image;
        return DOES_8G | NO_CHANGES;
    }

    public void run(ImageProcessor ip) {
        boolean isStack = image.getStackSize() > 1;

        GenericDialog gd = new GenericDialog("SRM");
        gd.addNumericField("Q", Q, 2);
        gd.addCheckbox("showAverages", true);
        if (isStack)
            gd.addCheckbox("3d", true);
        gd.showDialog();

        if (gd.wasCanceled())
            return;

        Q = (float)gd.getNextNumber();
        boolean showAverages = gd.getNextBoolean();
        boolean do3D = isStack ? gd.getNextBoolean() : false;
        if (do3D)
            srm3D(showAverages).show();
        else
            srm2D(ip, showAverages).show();
    }

    final float g = 256; // number of different intensity values
    protected float Q = 25; //25; // complexity of the assumed distributions
    protected float delta;

    /*
     * The predicate: is the difference of the averages of the two
     * regions R and R' smaller than
     *
     * g sqrt(1/2Q (1/|R| + 1/|R'|) ln 2/delta)
     *
     * Instead of calculating the square root all the time, we calculate
     * the factor g^2 / 2Q ln 2/delta, and compare
     *
     * (<R> - <R'>)^2 < factor (1/|R| + 1/|R'|)
     *
     * instead.
     */
    protected float factor, logDelta;

    /*
     * For performance reasons, these are held in w * h arrays
     */
    float[] average;
    int[] count;
    int[] regionIndex; // if < 0, it is -1 - actual_regionIndex

    /*
     * The statistical region merging wants to merge regions in a specific
     * order: for all neighboring pixel pairs, in ascending order of
     * intensity differences.
     *
     * In that order, it is tested if the regions these two pixels belong
     * to (by construction, the regions must be distinct) should be merged.
     *
     * For efficiency, we do it by bucket sorting, because there are only
     * g + 1 possible differences.
     *
     * After sorting, for each difference the pixel pair with the largest
     * index is stored in neighborBuckets[difference], and every
     * nextNeighbor[index] points to the pixel pair with the same
     * difference and the next smaller index (or -1 if there is none).
     *
     * The pixel pairs are identified by
     *
     *     2 * (x + (w - 1) * y) + direction
     *
     * where direction = 0 means "right neighbor", and direction = 1 means
     * " lower neighbor".  (We do not need "left" or "up", as the order
     * within the pair is not important.)
     *
     * In n dimensions, it must be n * pixel_count, and "direction"
     * specifies the Cartesian unit vector (axis) determining the neighbor.
     */
    int[] nextNeighbor, neighborBucket;

    protected ImagePlus srm3D(boolean showAverages) {
        int w = image.getWidth(), h = image.getHeight();
        int d = image.getStackSize();

        delta = 1f / (6 * w * h * d);
        /*
         * This would be the non-relaxed formula:
         *
         * factor = g * g / 2 / Q * (float)Math.log(2 / delta);
         *
         * The paper claims that this is more prone to oversegmenting.
         */
        factor = g * g / 2 / Q;
        logDelta = 2f * (float)Math.log(6 * w * h * d);

        IJ.showStatus("Initializing regions");
        initializeRegions3D(w, h, d);
        IJ.showStatus("Initializing neighbors");
        initializeNeighbors3D(w, h, d);
        IJ.showStatus("Merging neighbors");
        mergeAllNeighbors3D(w, h);
        IJ.showStatus("Making stack");

        ImageStack stack = new ImageStack(w, h);
        if (showAverages)
            for (int k = 0; k < d; k++) {
                int off = k * w * h;
                float[] p = new float[w * h];
                for (int i = 0; i < w * h; i++)
                    p[i] = average[getRegionIndex(i + off)];
                stack.addSlice(null, new FloatProcessor(w, h,
                            p, null));
            }
        else {
            int regionCount = consolidateRegions();

            if (regionCount > 1<<16)
                IJ.showMessage("Found " + regionCount
                    + " regions, which does not fit"
                    + " in 16-bit.");

            for (int k = 0; k < d; k++) {
                ImageProcessor ip;

                int off = k * w * h;

                if (regionCount > 1<<8) {
                    short[] p = new short[w * h];
                    for (int i = 0; i < p.length; i++)
                        p[i] = (short)regionIndex[i
                            + off];
                    ip = new ShortProcessor(w, h, p, null);
                }
                else {
                    byte[] p = new byte[w * h];
                    for (int i = 0; i < p.length; i++)
                        p[i] = (byte)regionIndex[i
                            + off];
                    ip = new ByteProcessor(w, h, p, null);
                }
                stack.addSlice(null, ip);
            }
        }

        IJ.showStatus("");
        String title = image.getTitle() + " (SRM3D Q=" + Q + ")";
        return new ImagePlus(title, stack);
    }

    protected ImagePlus srm2D(ImageProcessor ip, boolean showAverages) {
        int w = ip.getWidth(), h = ip.getHeight();

        delta = 1f / (6 * w * h);
        /*
         * This would be the non-relaxed formula:
         *
         * factor = g * g / 2 / Q * (float)Math.log(2 / delta);
         *
         * The paper claims that this is more prone to oversegmenting.
         */
        factor = g * g / 2 / Q;
        logDelta = 2f * (float)Math.log(6 * w * h);

        byte[] pixel = (byte[])ip.getPixels();
        initializeRegions2D(pixel, ip.getWidth(), ip.getHeight());
        initializeNeighbors2D(pixel, w, h);
        mergeAllNeighbors2D(w);

        if (showAverages) {
            for (int i = 0; i < average.length; i++)
                average[i] = average[getRegionIndex(i)];
            ip = new FloatProcessor(w, h, average, null);
        }
        else {
            int regionCount = consolidateRegions();

            if (regionCount > 1<<8) {
                if (regionCount > 1<<16)
                    IJ.showMessage("Found " + regionCount
                        + " regions, which does not fit"
                        + " in 16-bit.");
                short[] pixel16 = new short[w * h];
                for (int i = 0; i < pixel16.length; i++)
                    pixel16[i] = (short)regionIndex[i];
                ip = new ShortProcessor(w, h, pixel16, null);
            }
            else {
                pixel = new byte[w * h];
                for (int i = 0; i < pixel.length; i++)
                    pixel[i] = (byte)regionIndex[i];
                ip = new ByteProcessor(w, h, pixel, null);
            }
        }

        String title = image.getTitle() + " (SRM Q=" + Q + ")";
        return new ImagePlus(title, ip);
    }

    void initializeRegions2D(byte[] pixel, int w, int h) {
        average = new float[w * h];
        count = new int[w * h];
        regionIndex = new int[w * h];

        for (int i = 0; i < average.length; i++) {
            average[i] = pixel[i] & 0xff;
            count[i] = 1;
            regionIndex[i] = i;
        }
    }

    void initializeRegions3D(int w, int h, int d) {
        average = new float[w * h * d];
        count = new int[w * h * d];
        regionIndex = new int[w * h * d];

        for (int j = 0; j < d; j++) {
            byte[] pixel =
                (byte[])image.getStack().getProcessor(j
                        + 1).getPixels();
            int offset = j * w * h;
            for (int i = 0; i < w * h; i++) {
                average[offset + i] = pixel[i] & 0xff;
                count[offset + i] = 1;
                regionIndex[offset + i] = offset + i;
            }
        }
    }

    protected void addNeighborPair(int neighborIndex,
            byte[] pixel, int i1, int i2) {
        int difference = Math.abs((pixel[i1] & 0xff)
                - (pixel[i2] & 0xff));
        nextNeighbor[neighborIndex] = neighborBucket[difference];
        neighborBucket[difference] = neighborIndex;
    }

    void initializeNeighbors2D(byte[] pixel, int w, int h) {
        nextNeighbor = new int[2 * w * h];

        // bucket sort
        neighborBucket = new int[256];
        Arrays.fill(neighborBucket, -1);

        for (int j = h - 1; j >= 0; j--)
            for (int i = w - 1; i >= 0; i--) {
                int index = i + w * j;
                int neighborIndex = 2 * index;

                // vertical
                if (j < h - 1)
                    addNeighborPair(neighborIndex + 1,
                        pixel, index, index + w);

                // horizontal
                if (i < w - 1)
                    addNeighborPair(neighborIndex,
                        pixel, index, index + 1);
            }
    }

    protected void addNeighborPair(int neighborIndex,
            byte[] pixel, byte[] nextPixel, int i) {
        int difference = Math.abs((pixel[i] & 0xff)
                - (nextPixel[i] & 0xff));
        nextNeighbor[neighborIndex] = neighborBucket[difference];
        neighborBucket[difference] = neighborIndex;
    }

    void initializeNeighbors3D(int w, int h, int d) {
        nextNeighbor = new int[3 * w * h * d];

        // bucket sort
        neighborBucket = new int[256];
        Arrays.fill(neighborBucket, -1);

        byte[] nextPixel = null;
        for (int k = d - 1; k >= 0; k--) {
            byte[] pixel =
                (byte[])image.getStack().getProcessor(k
                        + 1).getPixels();
            for (int j = h - 1; j >= 0; j--)
                for (int i = w - 1; i >= 0; i--) {
                    int index = i + w * j;
                    int neighborIndex =
                        3 * (index + k * w * h);

                    // depth
                    if (nextPixel != null)
                        addNeighborPair(neighborIndex
                                + 2, pixel,
                            nextPixel, index);

                    // vertical
                    if (j < h - 1)
                        addNeighborPair(neighborIndex
                                + 1, pixel,
                            index, index + w);

                    // horizontal
                    if (i < w - 1)
                        addNeighborPair(neighborIndex,
                            pixel,
                            index, index + 1);
                }
            nextPixel = pixel;
        }
    }

    // recursively find out the region index for this pixel
    int getRegionIndex(int i) {
        i = regionIndex[i];
        while (i < 0)
            i = regionIndex[-1 - i];
        return i;
    }

    // should regions i1 and i2 be merged?
    boolean predicate(int i1, int i2) {
        float difference = average[i1] - average[i2];
        /*
         * This would be the non-relaxed predicate mentioned in the
         * paper.
         *
         * return difference * difference <
            factor * (1f / count[i1] + 1f / count[i2]);
         *
         */
        float log1 = (float)Math.log(1 + count[i1])
            * (g < count[i1] ? g : count[i1]);
        float log2 = (float)Math.log(1 + count[i2])
            * (g < count[i2] ? g : count[i2]);
        return difference * difference <
            .1f * factor * ((log1 + logDelta) / count[i1]
                + ((log2 + logDelta) / count[i2]));
    }

    void mergeAllNeighbors2D(int w) {
        for (int i = 0; i < neighborBucket.length; i++) {
            int neighborIndex = neighborBucket[i];
            while (neighborIndex >= 0) {
                int i1 = neighborIndex / 2;
                int i2 = i1
                    + (0 == (neighborIndex & 1) ? 1 : w);

                i1 = getRegionIndex(i1);
                i2 = getRegionIndex(i2);

                if (predicate(i1, i2))
                    mergeRegions(i1, i2);

                neighborIndex = nextNeighbor[neighborIndex];
            }
        }
    }

    void mergeAllNeighbors3D(int w, int h) {
        for (int i = 0; i < neighborBucket.length; i++) {
            int neighborIndex = neighborBucket[i];
            IJ.showProgress(i, neighborBucket.length);
            while (neighborIndex >= 0) {
                int i1 = neighborIndex / 3;
                int i2 = i1
                    + (0 == (neighborIndex % 3) ? 1 :
                      (1 == (neighborIndex % 3) ? w :
                       w * h));

                i1 = getRegionIndex(i1);
                i2 = getRegionIndex(i2);

                if (i1 != i2 && predicate(i1, i2))
                    mergeRegions(i1, i2);

                neighborIndex = nextNeighbor[neighborIndex];
            }
        }
        IJ.showProgress(neighborBucket.length, neighborBucket.length);
    }

    void mergeRegions(int i1, int i2) {
        if (i1 == i2)
            return;
        int mergedCount = count[i1] + count[i2];
        float mergedAverage = (average[i1] * count[i1]
                + average[i2] * count[i2]) / mergedCount;

        // merge larger index into smaller index
        if (i1 > i2) {
            average[i2] = mergedAverage;
            count[i2] = mergedCount;
            regionIndex[i1] = -1 - i2;
        }
        else {
            average[i1] = mergedAverage;
            count[i1] = mergedCount;
            regionIndex[i2] = -1 - i1;
        }
    }

    int consolidateRegions() {
        /*
         * By construction, a negative regionIndex will always point
         * to a smaller regionIndex.
         *
         * So we can get away by iterating from small to large and
         * replacing the positive ones with running numbers, and the
         * negative ones by the ones they are pointing to (that are
         * now guaranteed to contain a non-negative index).
         */
        int count = 0;
        for (int i = 0; i < regionIndex.length; i++)
            if (regionIndex[i] < 0)
                regionIndex[i] =
                    regionIndex[-1 - regionIndex[i]];
            else
                regionIndex[i] = count++;
        return count;
    }
}

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

0 个答案:

没有答案