用Python在Nan中高斯滤波图像

时间:2013-09-09 11:46:47

标签: python image-processing numpy matplotlib imagefilter

从2D坐标列表和第三个变量(力度),我创建了一个覆盖整个采样区域的2D numpy数组。我的目的是创建一个图像,其中每个像素包含其中的点的平均速度。之后使用高斯滤波器过滤该图像。

问题是该区域未被均匀采样。因此,我在图像中间有几个没有信息(Nan)的像素。当我尝试通过高斯滤波器过滤数组时,Nan传播会破坏整个图像。

我需要过滤此图片,但拒绝所有没有信息的像素。换句话说,如果一个像素不包含信息,那么过滤就不应该考虑它。

以下是我的平均代码示例:

Mean_V = np.zeros([len(x_bins), len(y_bins)])

for i, x_bin in enumerate(x_bins[:-1]):
    bin_x = (x > x_bins[i]) & (x <= x_bins[i+1])
    for j, y_bin in enumerate(y_bins[:-1]):
        bin_xy = (y[bin_x] > y_bins[j]) & (y[bin_x] <= y_bins[j+1])
        if (sum(x > 0 for x in bin_xy) > 0) :
            Mean_V[i,j]=np.mean(V[bin_x][bin_xy])
        else:
            Mean_V[i,j]=np.nan

编辑:

网上冲浪我已经结束了2013年我提出的这个问题。这个问题的解决方案可以在astropy库中找到:

http://docs.astropy.org/en/stable/convolution/

Astropy的卷积用来自邻居的内核加权插值替换NaN像素。

谢谢大家!!

4 个答案:

答案 0 :(得分:15)

用语言

通过将标准高斯滤波器应用于两个辅助阵列 V W ,可以轻松获得忽略给定阵列 U 中的NaN的高斯滤波器em>并通过取两者的比率得到结果 Z

这里, V 是原始 U 的副本,其中NaNs被零替换, W 是一个零的数组,其中零表示位置原始 U 中的NaNs。

这个想法是用零替换NaN会在滤波后的阵列中引入一个误差,但是可以通过将相同的高斯滤波器应用到另一个辅助阵列并将两者合并来补偿。

Python中的

package mmu.tom.linkedviewproject;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ToggleButton;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Created by Tom on 08/02/2016.
 */

public class GetAllDeviceListViewAdapter extends BaseAdapter {
    private JSONArray dataArray;
    private Activity activity;
    private String state;

    private static LayoutInflater inflater = null;

    public GetAllDeviceListViewAdapter(JSONArray jsonArray, Activity a) {
        this.dataArray = jsonArray;
        this.activity = a;
        inflater = (LayoutInflater) this.activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {
        return this.dataArray.length();
    }

    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // set up the convert view if it's null
        ListCell cell;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.get_all_devices_list_view_cell,null);
            cell = new ListCell();
            cell.deviceName = (TextView) convertView.findViewById(R.id.device_name);
            cell.deviceId = (TextView) convertView.findViewById(R.id.device_id);
            cell.type = (TextView) convertView.findViewById(R.id.type);
            cell.toggleButton = (ToggleButton) convertView.findViewById(R.id.toggleButton);
            cell.typeImg = (ImageView) convertView.findViewById(R.id.device_type);
            convertView.setTag(cell);
        } else {
            cell = (ListCell) convertView.getTag();
        }

        // changes the cell data here

        try {
            JSONObject jsonObject = this.dataArray.getJSONObject(position);
            cell.deviceName.setText(jsonObject.getString("name"));
            cell.deviceId.setText(" " + jsonObject.getString("deviceID"));
            cell.type.setText(" " + jsonObject.getString("type"));

            String toggle = jsonObject.getString("currentState");
            if (toggle.equals("on")) {
                cell.toggleButton.setChecked(true);
            } else {
                cell.toggleButton.setChecked(false);
            }

            String device = jsonObject.getString("type");
            if (device.equals("Light")) {
                cell.typeImg.setImageResource(R.mipmap.ic_lights_on);
            } else if (device.equals("Lock")) {
                cell.typeImg.setImageResource(R.mipmap.ic_lock_open_black_24dp);
            }
            // remember to set the image to type in future
        } catch(JSONException e) {
            e.printStackTrace();
        }

        return convertView;
    }

    private class ListCell {
        private TextView deviceName;
        private TextView deviceId;
        private TextView type;
        private ImageView typeImg;
        private ToggleButton toggleButton;
    }
}
数字

为了演示目的,高斯滤波器的系数设置为[0.25,0.50,0.25],它们总和为一个0.25 + 0.50 + 0.25 = 1,而不失一般性。

用零替换NaN并应用高斯滤波器(参见下面的VV)后,很明显零会引入误差,即由于“缺失”数据,系数0.25 + 0.50 = 0.75不能总结再一次,因此低估了“真实”的价值。

然而,这可以通过使用第二个辅助阵列来补偿(参见下面的WW),在使用相同的高斯滤波后,它只包含系数之和。

因此,划分两个滤波的辅助阵列会重新调整系数,使得它们总和为1,同时忽略NaN位置。

import numpy as np
import scipy as sp
import scipy.ndimage

U=sp.randn(10,10)          # random array...
U[U<2]=np.nan              # ...with NaNs for testing

V=U.copy()
V[np.isnan(U)]=0
VV=sp.ndimage.gaussian_filter(V,sigma=2.0)

W=0*U.copy()+1
W[np.isnan(U)]=0
WW=sp.ndimage.gaussian_filter(W,sigma=2.0)

Z=VV/WW

答案 1 :(得分:4)

mvvline_set

前段时间我跳过了这个问题,并使用了enter image description here(谢谢!)。 事实证明,将高斯滤波器应用于具有nans的数组的任务没有我想象的那么明确。

davids answer中所述,在图像的边界有多种处理值的选项(反射,常数外推等)。 对于图像中的nan值也必须做出类似的决定。

  • 有些想法可能是这样,按原样对nan值进行插值,但是问题来了,如何处理图像边界处的nans。
  • filter_nan_gaussian_david:Davids方法等效于在每个nan-point假设一些均值邻域值。这会导致总强度发生变化(请参见第3列中的sum值),但否则会做得很好。
  • filter_nan_gaussian_conserving:这种方法是通过高斯滤波器来提高每个点的完整性。映射到纳米像素的强度重新移回原点。如果这掩盖了感觉,则取决于应用程序。我有一个被nans包围的封闭区域,想预设总强度+避免边界处的变形。
  • filter_nan_gaussian_conserving2:通过高斯滤波器提高每个点的完整性。映射到纳米像素的强度将重定向到具有相同高斯权重的其他像素。这导致许多nan /边界像素附近原点处的强度相对降低。最后一行的最右边对此进行了说明。

代码

import numpy as np
from scipy import ndimage
import matplotlib as mpl
import matplotlib.pyplot as plt

def filter_nan_gaussian_conserving(arr, sigma):
    """Apply a gaussian filter to an array with nans.

    Intensity is only shifted between not-nan pixels and is hence conserved.
    The intensity redistribution with respect to each single point
    is done by the weights of available pixels according
    to a gaussian distribution.
    All nans in arr, stay nans in gauss.
    """
    nan_msk = np.isnan(arr)

    loss = np.zeros(arr.shape)
    loss[nan_msk] = 1
    loss = ndimage.gaussian_filter(
            loss, sigma=sigma, mode='constant', cval=1)

    gauss = arr.copy()
    gauss[nan_msk] = 0
    gauss = ndimage.gaussian_filter(
            gauss, sigma=sigma, mode='constant', cval=0)
    gauss[nan_msk] = np.nan

    gauss += loss * arr

    return gauss

def filter_nan_gaussian_conserving2(arr, sigma):
    """Apply a gaussian filter to an array with nans.

    Intensity is only shifted between not-nan pixels and is hence conserved.
    The intensity redistribution with respect to each single point
    is done by the weights of available pixels according
    to a gaussian distribution.
    All nans in arr, stay nans in gauss.
    """
    nan_msk = np.isnan(arr)

    loss = np.zeros(arr.shape)
    loss[nan_msk] = 1
    loss = ndimage.gaussian_filter(
            loss, sigma=sigma, mode='constant', cval=1)

    gauss = arr / (1-loss)
    gauss[nan_msk] = 0
    gauss = ndimage.gaussian_filter(
            gauss, sigma=sigma, mode='constant', cval=0)
    gauss[nan_msk] = np.nan

    return gauss

def filter_nan_gaussian_david(arr, sigma):
    """Allows intensity to leak into the nan area.
    According to Davids answer:
        https://stackoverflow.com/a/36307291/7128154
    """
    gauss = arr.copy()
    gauss[np.isnan(gauss)] = 0
    gauss = ndimage.gaussian_filter(
            gauss, sigma=sigma, mode='constant', cval=0)

    norm = np.ones(shape=arr.shape)
    norm[np.isnan(arr)] = 0
    norm = ndimage.gaussian_filter(
            norm, sigma=sigma, mode='constant', cval=0)

    # avoid RuntimeWarning: invalid value encountered in true_divide
    norm = np.where(norm==0, 1, norm)
    gauss = gauss/norm
    gauss[np.isnan(arr)] = np.nan
    return gauss



fig, axs = plt.subplots(3, 4)
fig.suptitle('black: 0, white: 1, red: nan')
cmap = mpl.cm.get_cmap('gist_yarg_r')
cmap.set_bad('r')
def plot_info(ax, arr, col):
    kws = dict(cmap=cmap, vmin=0, vmax=1)
    if col == 0:
        title = 'input'
    elif col == 1:
        title = 'filter_nan_gaussian_conserving'
    elif col == 2:
        title = 'filter_nan_gaussian_david'
    elif col == 3:
        title = 'filter_nan_gaussian_conserving2'
    ax.set_title(title + '\nsum: {:.4f}'.format(np.nansum(arr)))
    ax.imshow(arr, **kws)

sigma = (1,1)

arr0 = np.zeros(shape=(6, 10))
arr0[2:, :] = np.nan
arr0[2, 1:3] = 1

arr1 = np.zeros(shape=(6, 10))
arr1[2, 1:3] = 1
arr1[3, 2] = np.nan

arr2 = np.ones(shape=(6, 10)) *.5
arr2[3, 2] = np.nan

plot_info(axs[0, 0], arr0, 0)
plot_info(axs[0, 1], filter_nan_gaussian_conserving(arr0, sigma), 1)
plot_info(axs[0, 2], filter_nan_gaussian_david(arr0, sigma), 2)
plot_info(axs[0, 3], filter_nan_gaussian_conserving2(arr0, sigma), 3)

plot_info(axs[1, 0], arr1, 0)
plot_info(axs[1, 1], filter_nan_gaussian_conserving(arr1, sigma), 1)
plot_info(axs[1, 2], filter_nan_gaussian_david(arr1, sigma), 2)
plot_info(axs[1, 3], filter_nan_gaussian_conserving2(arr1, sigma), 3)

plot_info(axs[2, 0], arr2, 0)
plot_info(axs[2, 1], filter_nan_gaussian_conserving(arr2, sigma), 1)
plot_info(axs[2, 2], filter_nan_gaussian_david(arr2, sigma), 2)
plot_info(axs[2, 3], filter_nan_gaussian_conserving2(arr2, sigma), 3)

plt.show()

答案 2 :(得分:0)

如何用epsilon = 0.000001将Z = VV /(WW + epsilon)替换为Z = VV / WW以自动处理案件,而在先前的建议中没有任何观察

答案 3 :(得分:-4)

最简单的方法是通过nan_to_numnan变为零。这是否有意义是一个单独的问题。