diff --git a/nifty/operators/__init__.py b/nifty/operators/__init__.py
index 44d0c545552e9a9b0f1a95efc526df2d00e65a5a..ed25e1c63cd1eee1b2b698d64b5abd1348b0843f 100644
--- a/nifty/operators/__init__.py
+++ b/nifty/operators/__init__.py
@@ -3,6 +3,7 @@ from .linear_operator import LinearOperator
 from .diagonal_operator import DiagonalOperator
 from .endomorphic_operator import EndomorphicOperator
 from .fft_smoothing_operator import FFTSmoothingOperator
+from .direct_smoothing_operator import DirectSmoothingOperator
 from .fft_operator import FFTOperator
 from .inversion_enabler import InversionEnabler
 from .composed_operator import ComposedOperator
diff --git a/nifty/operators/direct_smoothing_operator.py b/nifty/operators/direct_smoothing_operator.py
new file mode 100644
index 0000000000000000000000000000000000000000..73ae7c4c67b7a884e7ff144f9c2faca562316764
--- /dev/null
+++ b/nifty/operators/direct_smoothing_operator.py
@@ -0,0 +1,108 @@
+from __future__ import division
+from builtins import range
+import numpy as np
+
+from .endomorphic_operator import EndomorphicOperator
+from .. import dobj
+from ..spaces import PowerSpace
+from .. import utilities
+from .. import Field, DomainTuple
+
+
+class DirectSmoothingOperator(EndomorphicOperator):
+    def __init__(self, domain, sigma, log_distances=False,
+                 space=None):
+        super(DirectSmoothingOperator, self).__init__()
+
+        self._domain = DomainTuple.make(domain)
+        self._space = utilities.infer_space(self._domain, space)
+        if not isinstance(self._domain[self._space], PowerSpace):
+            raise TypeError("PowerSpace needed")
+
+        self._sigma = float(sigma)
+        self._log_distances = log_distances
+        self._effective_smoothing_width = 3.01
+
+        self._axis = self._domain.axes[self._space][0]
+        if self._sigma != 0:
+            distances = self._domain[self._space].k_lengths
+            if self._log_distances:
+                distances = np.log(np.maximum(distances, 1e-15))
+
+            self._ibegin, self._nval, self._wgt = self._precompute(distances)
+
+    def _times(self, x):
+        if self._sigma == 0:
+            return x.copy()
+
+        return self._smooth(x)
+
+    @property
+    def domain(self):
+        return self._domain
+
+    @property
+    def self_adjoint(self):
+        return True
+
+    @property
+    def unitary(self):
+        return False
+
+    def _precompute(self, x):
+        """ Does precomputations for Gaussian smoothing on a 1D irregular grid.
+
+        Parameters
+        ----------
+        x: 1D floating point array or list containing the individual grid
+            positions. Points must be given in ascending order.
+
+
+        Returns
+        -------
+        ibegin: integer array of the same size as x
+            ibegin[i] is the minimum grid index to consider when computing the
+            smoothed value at grid index i
+        nval: integer array of the same size as x
+            nval[i] is the number of indices to consider when computing the
+            smoothed value at grid index i.
+        wgt: list with the same number of entries as x
+            wgt[i] is an array with nval[i] entries containing the
+            normalized smoothing weights.
+        """
+        dxmax = self._effective_smoothing_width*self._sigma
+
+        x = np.asarray(x)
+
+        ibegin = np.searchsorted(x, x-dxmax)
+        nval = np.searchsorted(x, x+dxmax) - ibegin
+
+        wgt = []
+        expfac = 1. / (2. * self._sigma*self._sigma)
+        for i in range(x.size):
+            if nval[i] > 0:
+                t = x[ibegin[i]:ibegin[i]+nval[i]]-x[i]
+                t = np.exp(-t*t*expfac)
+                t *= 1./np.sum(t)
+                wgt.append(t)
+            else:
+                wgt.append(np.array([]))
+
+        return ibegin, nval, wgt
+
+    def _smooth(self, x):
+        if dobj.distaxis(x.val) == self._axis:
+            val = dobj.redistribute(x.val, nodist=(self._axis,))
+        else:
+            val = x.val.copy()
+        lval = dobj.local_data(val)
+
+        for sl in utilities.get_slice_list(lval.shape, (self._axis,)):
+            inp = lval[sl]
+            out = np.zeros(inp.shape[0], dtype=inp.dtype)
+            for i in range(inp.shape[0]):
+                out[self._ibegin[i]:self._ibegin[i]+self._nval[i]] += \
+                    inp[i] * self._wgt[i][:]
+            lval[sl] = out
+        val = dobj.redistribute(val, dobj.distaxis(x.val))
+        return Field(x.domain, val)
diff --git a/test/test_operators/test_smoothing_operator.py b/test/test_operators/test_smoothing_operator.py
index 79ad0c311aa096d0f5994c60e0c1231c667a5d5c..cbae4195bf470b00642da7cb400f25e71a2e20bb 100644
--- a/test/test_operators/test_smoothing_operator.py
+++ b/test/test_operators/test_smoothing_operator.py
@@ -83,3 +83,29 @@ class SmoothingOperator_Tests(unittest.TestCase):
                                     mean=4, dtype=tp)
         out = smo(inp)
         assert_allclose(inp.sum(), out.sum(), rtol=tol, atol=tol)
+
+    @expand(product([100, 200], [False, True], [0., 1.,  3.7],
+                    [np.float64, np.complex128]))
+    def test_smooth_irregular1(self, sz, log, sigma, tp):
+        tol = _get_rtol(tp)
+        sp = ift.RGSpace(sz, harmonic=True)
+        bb = ift.PowerSpace.useful_binbounds(sp, logarithmic=log)
+        ps = ift.PowerSpace(sp, binbounds=bb)
+        smo = ift.DirectSmoothingOperator(ps, sigma=sigma)
+        inp = ift.Field.from_random(domain=ps, random_type='normal', std=1,
+                                    mean=4, dtype=tp)
+        out = smo(inp)
+        assert_allclose(inp.sum(), out.sum(), rtol=tol, atol=tol)
+
+    @expand(product([10, 15], [7, 10], [False, True], [0., 1.,  3.7],
+                    [np.float64, np.complex128]))
+    def test_smooth_irregular2(self, sz1, sz2, log, sigma, tp):
+        tol = _get_rtol(tp)
+        sp = ift.RGSpace([sz1, sz2], harmonic=True)
+        bb = ift.PowerSpace.useful_binbounds(sp, logarithmic=log)
+        ps = ift.PowerSpace(sp, binbounds=bb)
+        smo = ift.DirectSmoothingOperator(ps, sigma=sigma)
+        inp = ift.Field.from_random(domain=ps, random_type='normal', std=1,
+                                    mean=4, dtype=tp)
+        out = smo(inp)
+        assert_allclose(inp.sum(), out.sum(), rtol=tol, atol=tol)