常用的畫素操作演算法:Resize、Flip、Rotate

Tony沈哲發表於2019-03-02

Resize

影象縮放是把原影象按照目標尺寸放大或者縮小,是影象處理的一種。

影象縮放有多種演算法。最為簡單的是最臨近插值演算法,它是根據原影象和目標影象的尺寸,計算縮放的比例,然後根據縮放比例計算目標畫素所依據的原畫素,過程中自然會產生小數,這時就採用四捨五入,取與這個點最相近的點。

除此之外,還有雙線性插值演算法。

雙線性插值,又稱為雙線性內插。在數學上,雙線性插值是有兩個變數的插值函式的線性插值擴充套件,其核心思想是在兩個方向分別進行一次線性插值。

其公式如下: f(i+u,j+v) =(1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)

其中U和V表示浮點座標的小數部分,顯然離目標點距離越近的點的權重越大,這也正符合目標點的值與離他最近的點最接近這一事實。

cv4j的resize目前支援這兩種演算法。通過Resize類的原始碼,可以看到有兩個常量

	public final static int NEAREST_INTEPOLATE = 1; // 最臨近插值演算法
	public final static int BILINE_INTEPOLATE = 2;     // 雙線性插值演算法
複製程式碼

使用最臨近插值演算法,將原圖縮小到0.75倍。

        CV4JImage cv4jImage = new CV4JImage(bitmap);
        ImageProcessor imageProcessor = cv4jImage.getProcessor();

        Resize resize = new Resize(0.75f);

        imageProcessor = resize.resize(imageProcessor,Resize.NEAREST_INTEPOLATE);

        if (imageProcessor!=null) {
            CV4JImage resultCV4JImage = new CV4JImage(imageProcessor.getWidth(), imageProcessor.getHeight(), imageProcessor.getPixels());
            result1.setImageBitmap(resultCV4JImage.getProcessor().getImage().toBitmap());
        }
複製程式碼

使用雙線性插值演算法,將原圖放大2倍。

        cv4jImage = new CV4JImage(bitmap);
        ImageProcessor imageProcessor2 = cv4jImage.getProcessor();

        resize = new Resize(2f);

        imageProcessor2 = resize.resize(imageProcessor,Resize.BILINE_INTEPOLATE);

        if (imageProcessor2!=null) {
            CV4JImage resultCV4JImage = new CV4JImage(imageProcessor2.getWidth(), imageProcessor2.getHeight(), imageProcessor2.getPixels());
            result2.setImageBitmap(resultCV4JImage.getProcessor().getImage().toBitmap());
        }
複製程式碼

效果如下:

影象縮放.png

Flip

Flip是翻轉的意思,也被稱為映象變換。又可以分為水平映象和垂直映象,水平映象即將影象左半部分和右半部分以影象豎直中軸線為中心軸進行兌換,而豎直映象則是將影象上半部分和下半部分以影象水平中軸線為中心軸進行兌換。

flip的演算法很簡單

	public final static int FLIP_VERTICAL = -1;
	public final static int FLIP_HORIZONTAL = 1;
	
	public static void flip(ImageProcessor processor, int option) {
		int width = processor.getWidth();
        int height = processor.getHeight();
        int ch = processor.getChannels();
        int index1 = 0;
        int index2 = 0;
        int total = width*height;
        byte[][] output = new byte[ch][total];
        for(int row=0; row<height; row++) {
        	for(int col=0; col<width; col++) {
        		index1 = row*width+col;
        		if(option == FLIP_HORIZONTAL) {
        			index2 = row*width + width-col-1;
        		} else if(option == FLIP_VERTICAL){
        			index2 = (height-row-1)*width + col;
        		} else {
        			throw new CV4JException("invalid option : " + option);
        		}
        		for(int i=0; i<ch; i++) {
        			output[i][index2] = processor.toByte(i)[index1];
        		}
        	}
        }
        if(ch == 3) {
        	((ColorProcessor) processor).putRGB(output[0], output[1], output[2]);
        } else {
        	((ByteProcessor) processor).putGray(output[0]);
        }
	}
複製程式碼

實現具體的左右翻轉

        CV4JImage cv4jImage = new CV4JImage(bitmap);
        ImageProcessor imageProcessor = cv4jImage.getProcessor();

        Flip.flip(imageProcessor,Flip.FLIP_HORIZONTAL);

        if (imageProcessor!=null) {
            CV4JImage resultCV4JImage = new CV4JImage(imageProcessor.getWidth(), imageProcessor.getHeight(), imageProcessor.getPixels());
            result1.setImageBitmap(resultCV4JImage.getProcessor().getImage().toBitmap());
        }
複製程式碼

實現具體的上下翻轉

        cv4jImage = new CV4JImage(bitmap);
        ImageProcessor imageProcessor2 = cv4jImage.getProcessor();

        Flip.flip(imageProcessor2,Flip.FLIP_VERTICAL);

        if (imageProcessor2!=null) {
            CV4JImage resultCV4JImage = new CV4JImage(imageProcessor2.getWidth(), imageProcessor2.getHeight(), imageProcessor2.getPixels());
            result2.setImageBitmap(resultCV4JImage.getProcessor().getImage().toBitmap());
        }
複製程式碼

效果如下:

影象翻轉.png

Rotate

影象旋轉是指影象以某一點為中心旋轉一定的角度,形成一幅新的影象的過程。當然這個點通常就是影象的中心。既然是按照中心旋轉,自然會有這樣一個屬性:旋轉前和旋轉後的點離中心的位置不變。

影象的旋轉是影象幾何變換的一種,旋轉前後的影象的畫素的RGB都是沒有改變的,改變的只是每一個畫素的所在位置。

cv4j提供兩種旋轉的演算法:NormRotate和FastRotate

下面以NormRotate為例,使用起來很簡單,旋轉120度,背景為紅色。

        CV4JImage cv4jImage = new CV4JImage(bitmap);
        ImageProcessor imageProcessor = cv4jImage.getProcessor();

        NormRotate normRotate = new NormRotate();
        imageProcessor = normRotate.rotate(imageProcessor,120, Scalar.rgb(255,0,0));

        if (imageProcessor!=null) {
            CV4JImage resultCV4JImage = new CV4JImage(imageProcessor.getWidth(), imageProcessor.getHeight(), imageProcessor.getPixels());
            result.setImageBitmap(resultCV4JImage.getProcessor().getImage().toBitmap());
        }
複製程式碼

效果如下:

影象旋轉.png

總結

cv4j 是gloomyfish和我一起開發的影象處理庫,純java實現,我們已經分離了一個Android版本和一個Java版本。

畫素操作是 cv4j 的基本功能之一,本文介紹了三種常見的變換。我們可以通過影象的Resize、Flip、Rotate變換來豐富圖片資料的多樣性。

如果您想看該系列先前的文章可以訪問下面的文集: www.jianshu.com/nb/10401400

相關文章