Changeset View
Changeset View
Standalone View
Standalone View
plugins/tools/tool_smart_patch/kis_inpaint.cpp
Show First 20 Lines • Show All 47 Lines • ▼ Show 20 Line(s) | |||||
48 | #include "KoColorSpace.h" | 48 | #include "KoColorSpace.h" | ||
49 | #include "KoChannelInfo.h" | 49 | #include "KoChannelInfo.h" | ||
50 | #include "KoMixColorsOp.h" | 50 | #include "KoMixColorsOp.h" | ||
51 | #include "KoColorModelStandardIds.h" | 51 | #include "KoColorModelStandardIds.h" | ||
52 | #include "KoColorSpaceRegistry.h" | 52 | #include "KoColorSpaceRegistry.h" | ||
53 | #include "KoColorSpaceTraits.h" | 53 | #include "KoColorSpaceTraits.h" | ||
54 | 54 | | |||
55 | const int MAX_DIST = 65535; | 55 | const int MAX_DIST = 65535; | ||
56 | const quint8 MASK_SET = 0; | 56 | const quint8 MASK_SET = 255; | ||
57 | const quint8 MASK_CLEAR = 255; | 57 | const quint8 MASK_CLEAR = 0; | ||
58 | | ||||
59 | void patchImage(KisPaintDeviceSP, KisPaintDeviceSP, int radius); | | |||
60 | 58 | | |||
61 | class MaskedImage; //forward decl for the forward decl below | 59 | class MaskedImage; //forward decl for the forward decl below | ||
62 | template <typename T> float distance_impl(const MaskedImage& my, int x, int y, const MaskedImage& other, int xo, int yo); | 60 | template <typename T> float distance_impl(const MaskedImage& my, int x, int y, const MaskedImage& other, int xo, int yo); | ||
63 | 61 | | |||
64 | 62 | | |||
65 | class ImageView | 63 | class ImageView | ||
66 | { | 64 | { | ||
67 | 65 | | |||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Line(s) | 72 | public: | |||
147 | 145 | | |||
148 | inline int pixel_size(void) const | 146 | inline int pixel_size(void) const | ||
149 | { | 147 | { | ||
150 | return m_pixelSize; | 148 | return m_pixelSize; | ||
151 | } | 149 | } | ||
152 | 150 | | |||
153 | void saveToDevice(KisPaintDeviceSP outDev, QRect rect) | 151 | void saveToDevice(KisPaintDeviceSP outDev, QRect rect) | ||
154 | { | 152 | { | ||
155 | | ||||
156 | Q_ASSERT(outDev->colorSpace()->pixelSize() == (quint32) m_pixelSize); | 153 | Q_ASSERT(outDev->colorSpace()->pixelSize() == (quint32) m_pixelSize); | ||
157 | outDev->writeBytes(m_data, rect); | 154 | outDev->writeBytes(m_data, rect); | ||
158 | } | 155 | } | ||
159 | 156 | | |||
160 | void DebugDump(const QString& fnamePrefix) | 157 | void DebugDump(const QString& fnamePrefix) | ||
161 | { | 158 | { | ||
162 | QRect imSize(QPoint(0, 0), QSize(m_imageWidth, m_imageHeight)); | 159 | QRect imSize(QPoint(0, 0), QSize(m_imageWidth, m_imageHeight)); | ||
163 | const KoColorSpace* cs = (m_pixelSize == 1) ? | 160 | const KoColorSpace* cs = (m_pixelSize == 1) ? | ||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Line(s) | 212 | private: | |||
221 | 218 | | |||
222 | const KoColorSpace* cs; | 219 | const KoColorSpace* cs; | ||
223 | const KoColorSpace* csMask; | 220 | const KoColorSpace* csMask; | ||
224 | 221 | | |||
225 | ImageData maskData; | 222 | ImageData maskData; | ||
226 | ImageData imageData; | 223 | ImageData imageData; | ||
227 | 224 | | |||
228 | 225 | | |||
229 | void cacheImageSize(KisPaintDeviceSP imageDev) | 226 | void cacheImage(KisPaintDeviceSP imageDev, QRect rect) | ||
230 | { | 227 | { | ||
231 | imageSize = imageDev->exactBounds(); | | |||
232 | } | | |||
233 | | ||||
234 | void cacheImage(KisPaintDeviceSP imageDev) | | |||
235 | { | | |||
236 | Q_ASSERT(!imageSize.isEmpty() && imageSize.isValid()); | | |||
237 | cs = imageDev->colorSpace(); | 228 | cs = imageDev->colorSpace(); | ||
238 | nChannels = cs->channelCount(); | 229 | nChannels = cs->channelCount(); | ||
239 | imageData.Init(imageDev, imageSize); | 230 | imageData.Init(imageDev, rect); | ||
231 | imageSize = rect; | ||||
240 | } | 232 | } | ||
241 | 233 | | |||
242 | 234 | | |||
243 | void cacheMask(KisPaintDeviceSP maskDev) | 235 | void cacheMask(KisPaintDeviceSP maskDev, QRect rect) | ||
244 | { | 236 | { | ||
245 | Q_ASSERT(!imageSize.isEmpty() && imageSize.isValid()); | | |||
246 | Q_ASSERT(maskDev->colorSpace()->pixelSize() == 1); | 237 | Q_ASSERT(maskDev->colorSpace()->pixelSize() == 1); | ||
247 | csMask = maskDev->colorSpace(); | 238 | csMask = maskDev->colorSpace(); | ||
248 | maskData.Init(maskDev, imageSize); | 239 | maskData.Init(maskDev, rect); | ||
249 | 240 | | |||
250 | //hard threshold for the initial mask | 241 | //hard threshold for the initial mask | ||
251 | //may be optional. needs testing | 242 | //may be optional. needs testing | ||
252 | std::for_each(maskData.data(), maskData.data() + maskData.num_bytes(), [](quint8 & v) { | 243 | std::for_each(maskData.data(), maskData.data() + maskData.num_bytes(), [](quint8 & v) { | ||
253 | v = (v < MASK_CLEAR) ? MASK_SET : MASK_CLEAR; | 244 | v = (v > MASK_CLEAR) ? MASK_SET : MASK_CLEAR; | ||
254 | }); | 245 | }); | ||
255 | } | 246 | } | ||
256 | 247 | | |||
257 | MaskedImage() {} | 248 | MaskedImage() {} | ||
258 | 249 | | |||
259 | public: | 250 | public: | ||
260 | std::function< float(const MaskedImage&, int, int, const MaskedImage& , int , int ) > distance; | 251 | std::function< float(const MaskedImage&, int, int, const MaskedImage& , int , int ) > distance; | ||
261 | 252 | | |||
262 | void toPaintDevice(KisPaintDeviceSP imageDev, QRect rect) | 253 | void toPaintDevice(KisPaintDeviceSP imageDev, QRect rect) | ||
263 | { | 254 | { | ||
264 | imageData.saveToDevice(imageDev, rect); | 255 | imageData.saveToDevice(imageDev, rect); | ||
265 | } | 256 | } | ||
266 | 257 | | |||
267 | void DebugDump(const QString& name) | 258 | void DebugDump(const QString& name) | ||
268 | { | 259 | { | ||
269 | imageData.DebugDump(name + "_img"); | 260 | imageData.DebugDump(name + "_img"); | ||
270 | maskData.DebugDump(name + "_mask"); | 261 | maskData.DebugDump(name + "_mask"); | ||
271 | } | 262 | } | ||
272 | 263 | | |||
273 | void clearMask(void) | 264 | void clearMask(void) | ||
274 | { | 265 | { | ||
275 | std::fill(maskData.data(), maskData.data() + maskData.num_bytes(), MASK_CLEAR); | 266 | std::fill(maskData.data(), maskData.data() + maskData.num_bytes(), MASK_CLEAR); | ||
276 | } | 267 | } | ||
277 | 268 | | |||
278 | void initialize(KisPaintDeviceSP _imageDev, KisPaintDeviceSP _maskDev) | 269 | void initialize(KisPaintDeviceSP _imageDev, KisPaintDeviceSP _maskDev, QRect _maskRect) | ||
279 | { | 270 | { | ||
280 | cacheImageSize(_imageDev); | 271 | cacheImage(_imageDev, _maskRect); | ||
281 | cacheImage(_imageDev); | 272 | cacheMask(_maskDev, _maskRect); | ||
282 | cacheMask(_maskDev); | | |||
283 | 273 | | |||
284 | //distance function is the only that needs to know the type | 274 | //distance function is the only that needs to know the type | ||
285 | //For performance reasons we can't use functions provided by color space | 275 | //For performance reasons we can't use functions provided by color space | ||
286 | KoID colorDepthId = _imageDev->colorSpace()->colorDepthId(); | 276 | KoID colorDepthId = _imageDev->colorSpace()->colorDepthId(); | ||
287 | 277 | | |||
288 | //Use RGB traits to assign actual pixel data types. | 278 | //Use RGB traits to assign actual pixel data types. | ||
289 | distance = &distance_impl<KoRgbU8Traits::channels_type>; | 279 | distance = &distance_impl<KoRgbU8Traits::channels_type>; | ||
290 | 280 | | |||
291 | if( colorDepthId == Integer16BitsColorDepthID ) | 281 | if( colorDepthId == Integer16BitsColorDepthID ) | ||
292 | distance = &distance_impl<KoRgbU16Traits::channels_type>; | 282 | distance = &distance_impl<KoRgbU16Traits::channels_type>; | ||
293 | #ifdef HAVE_OPENEXR | 283 | #ifdef HAVE_OPENEXR | ||
294 | if( colorDepthId == Float16BitsColorDepthID ) | 284 | if( colorDepthId == Float16BitsColorDepthID ) | ||
295 | distance = &distance_impl<KoRgbF16Traits::channels_type>; | 285 | distance = &distance_impl<KoRgbF16Traits::channels_type>; | ||
296 | #endif | 286 | #endif | ||
297 | if( colorDepthId == Float32BitsColorDepthID ) | 287 | if( colorDepthId == Float32BitsColorDepthID ) | ||
298 | distance = &distance_impl<KoRgbF32Traits::channels_type>; | 288 | distance = &distance_impl<KoRgbF32Traits::channels_type>; | ||
299 | 289 | | |||
300 | if( colorDepthId == Float64BitsColorDepthID ) | 290 | if( colorDepthId == Float64BitsColorDepthID ) | ||
301 | distance = &distance_impl<KoRgbF64Traits::channels_type>; | 291 | distance = &distance_impl<KoRgbF64Traits::channels_type>; | ||
302 | } | 292 | } | ||
303 | 293 | | |||
304 | MaskedImage(KisPaintDeviceSP _imageDev, KisPaintDeviceSP _maskDev) | 294 | MaskedImage(KisPaintDeviceSP _imageDev, KisPaintDeviceSP _maskDev, QRect _maskRect) | ||
305 | { | 295 | { | ||
306 | initialize(_imageDev, _maskDev); | 296 | initialize(_imageDev, _maskDev, _maskRect); | ||
307 | // DebugDump("Initialize"); | | |||
308 | } | 297 | } | ||
309 | 298 | | |||
310 | void downsample2x(void) | 299 | void downsample2x(void) | ||
311 | { | 300 | { | ||
312 | int H = imageSize.height(); | 301 | int H = imageSize.height(); | ||
313 | int W = imageSize.width(); | 302 | int W = imageSize.width(); | ||
314 | int newW = W / 2, newH = H / 2; | 303 | int newW = W / 2, newH = H / 2; | ||
315 | 304 | | |||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Line(s) | 379 | { | |||
397 | clone->csMask = this->csMask; | 386 | clone->csMask = this->csMask; | ||
398 | clone->distance = this->distance; | 387 | clone->distance = this->distance; | ||
399 | return clone; | 388 | return clone; | ||
400 | } | 389 | } | ||
401 | 390 | | |||
402 | int countMasked(void) | 391 | int countMasked(void) | ||
403 | { | 392 | { | ||
404 | int count = std::count_if(maskData.data(), maskData.data() + maskData.num_elements(), [](quint8 v) { | 393 | int count = std::count_if(maskData.data(), maskData.data() + maskData.num_elements(), [](quint8 v) { | ||
405 | return v < MASK_CLEAR; | 394 | return v > MASK_CLEAR; | ||
406 | }); | 395 | }); | ||
407 | return count; | 396 | return count; | ||
408 | } | 397 | } | ||
409 | 398 | | |||
410 | inline bool isMasked(int x, int y) | 399 | inline bool isMasked(int x, int y) | ||
411 | { | 400 | { | ||
412 | return (*maskData(x, y) < MASK_CLEAR); | 401 | return (*maskData(x, y) > MASK_CLEAR); | ||
413 | } | 402 | } | ||
414 | 403 | | |||
415 | //returns true if the patch contains a masked pixel | 404 | //returns true if the patch contains a masked pixel | ||
416 | bool containsMasked(int x, int y, int S) | 405 | bool containsMasked(int x, int y, int S) | ||
417 | { | 406 | { | ||
418 | for (int dy = -S; dy <= S; ++dy) { | 407 | for (int dy = -S; dy <= S; ++dy) { | ||
419 | int ys = y + dy; | 408 | int ys = y + dy; | ||
420 | if (ys < 0 || ys >= imageSize.height()) | 409 | if (ys < 0 || ys >= imageSize.height()) | ||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Line(s) | 458 | for (auto& v : w) { | |||
474 | weights.push_back(v2); | 463 | weights.push_back(v2); | ||
475 | } | 464 | } | ||
476 | 465 | | |||
477 | mixOp->mixColors(pixels.data(), weights.data(), n, dst); | 466 | mixOp->mixColors(pixels.data(), weights.data(), n, dst); | ||
478 | } | 467 | } | ||
479 | 468 | | |||
480 | inline void setMask(int x, int y, quint8 v) | 469 | inline void setMask(int x, int y, quint8 v) | ||
481 | { | 470 | { | ||
482 | *(maskData(x, y)) = v; | 471 | *(maskData(x, y)) = v; | ||
rempt: Does this mean the inpainting tool should be disabled for now when the color depth is other… | |||||
483 | } | 472 | } | ||
484 | 473 | | |||
485 | inline int channelCount(void) const | 474 | inline int channelCount(void) const | ||
486 | { | 475 | { | ||
487 | return cs->channelCount(); | 476 | return cs->channelCount(); | ||
488 | } | 477 | } | ||
489 | }; | 478 | }; | ||
490 | 479 | | |||
▲ Show 20 Lines • Show All 275 Lines • ▼ Show 20 Line(s) | 753 | private: | |||
766 | MaskedImageSP initial; | 755 | MaskedImageSP initial; | ||
767 | NearestNeighborFieldSP nnf_TargetToSource; | 756 | NearestNeighborFieldSP nnf_TargetToSource; | ||
768 | NearestNeighborFieldSP nnf_SourceToTarget; | 757 | NearestNeighborFieldSP nnf_SourceToTarget; | ||
769 | int radius; | 758 | int radius; | ||
770 | QList<MaskedImageSP> pyramid; | 759 | QList<MaskedImageSP> pyramid; | ||
771 | 760 | | |||
772 | 761 | | |||
773 | public: | 762 | public: | ||
774 | Inpaint(KisPaintDeviceSP dev, KisPaintDeviceSP devMask, int _radius) | 763 | Inpaint(KisPaintDeviceSP dev, KisPaintDeviceSP devMask, int _radius, QRect maskRect) | ||
775 | { | 764 | { | ||
776 | initial = new MaskedImage(dev, devMask); | 765 | initial = new MaskedImage(dev, devMask, maskRect); | ||
777 | radius = _radius; | 766 | radius = _radius; | ||
778 | devCache = dev; | 767 | devCache = dev; | ||
779 | } | 768 | } | ||
780 | MaskedImageSP patch(void); | 769 | MaskedImageSP patch(void); | ||
781 | MaskedImageSP patch_simple(void); | 770 | MaskedImageSP patch_simple(void); | ||
782 | }; | 771 | }; | ||
783 | 772 | | |||
784 | 773 | | |||
Show All 24 Lines | 776 | { | |||
809 | 798 | | |||
810 | //recursively building nearest neighbor field | 799 | //recursively building nearest neighbor field | ||
811 | for (int level = maxlevel - 1; level > 0; level--) { | 800 | for (int level = maxlevel - 1; level > 0; level--) { | ||
812 | source = pyramid.at(level); | 801 | source = pyramid.at(level); | ||
813 | 802 | | |||
814 | if (level == maxlevel - 1) { | 803 | if (level == maxlevel - 1) { | ||
815 | //random initial guess | 804 | //random initial guess | ||
816 | nnf_TargetToSource = new NearestNeighborField(target, source, radius); | 805 | nnf_TargetToSource = new NearestNeighborField(target, source, radius); | ||
817 | nnf_TargetToSource->randomize(); | 806 | nnf_TargetToSource->randomize(); | ||
rempt: Page 6 of what? | |||||
818 | } else { | 807 | } else { | ||
819 | // then, we use the rebuilt (upscaled) target | 808 | // then, we use the rebuilt (upscaled) target | ||
820 | // and reuse the previous NNF as initial guess | 809 | // and reuse the previous NNF as initial guess | ||
821 | 810 | | |||
822 | NearestNeighborFieldSP new_nnf_rev = new NearestNeighborField(target, source, radius); | 811 | NearestNeighborFieldSP new_nnf_rev = new NearestNeighborField(target, source, radius); | ||
823 | new_nnf_rev->initialize(*nnf_TargetToSource); | 812 | new_nnf_rev->initialize(*nnf_TargetToSource); | ||
824 | nnf_TargetToSource = new_nnf_rev; | 813 | nnf_TargetToSource = new_nnf_rev; | ||
825 | } | 814 | } | ||
▲ Show 20 Lines • Show All 147 Lines • ▼ Show 20 Line(s) | 914 | } else { | |||
973 | target->mixColors(pixels, weights, wsum, target->getImagePixel(x, y)); | 962 | target->mixColors(pixels, weights, wsum, target->getImagePixel(x, y)); | ||
974 | } | 963 | } | ||
975 | } | 964 | } | ||
976 | } | 965 | } | ||
977 | } | 966 | } | ||
978 | 967 | | |||
979 | QRect getMaskBoundingBox(KisPaintDeviceSP maskDev) | 968 | QRect getMaskBoundingBox(KisPaintDeviceSP maskDev) | ||
980 | { | 969 | { | ||
981 | KoColor defaultMaskPixel = maskDev->defaultPixel(); | | |||
982 | maskDev->setDefaultPixel(KoColor(Qt::white, maskDev->colorSpace())); | | |||
983 | QRect maskRect = maskDev->nonDefaultPixelArea(); | 970 | QRect maskRect = maskDev->nonDefaultPixelArea(); | ||
984 | maskDev->setDefaultPixel(defaultMaskPixel); | | |||
985 | | ||||
986 | return maskRect; | 971 | return maskRect; | ||
987 | } | 972 | } | ||
988 | 973 | | |||
989 | 974 | | |||
990 | QRect patchImage(KisPaintDeviceSP imageDev, KisPaintDeviceSP maskDev, int patchRadius, int accuracy) | 975 | QRect patchImage(const KisPaintDeviceSP imageDev, const KisPaintDeviceSP maskDev, int patchRadius, int accuracy) | ||
991 | { | 976 | { | ||
992 | QRect maskRect = getMaskBoundingBox(maskDev); | 977 | QRect maskRect = getMaskBoundingBox(maskDev); | ||
993 | QRect imageRect = imageDev->exactBounds(); | 978 | QRect imageRect = imageDev->exactBounds(); | ||
994 | 979 | | |||
995 | float scale = 1 + (accuracy / 25); //higher accuracy means we include more surrouding area around the patch. Minimum 2x padding. | 980 | float scale = 1 + (accuracy / 25); //higher accuracy means we include more surrouding area around the patch. Minimum 2x padding. | ||
996 | int dx = maskRect.width() * scale; | 981 | int dx = maskRect.width() * scale; | ||
997 | int dy = maskRect.height() * scale; | 982 | int dy = maskRect.height() * scale; | ||
998 | maskRect.adjust(-dx, -dy, dx, dy); | 983 | maskRect.adjust(-dx, -dy, dx, dy); | ||
999 | maskRect = maskRect.intersected(imageRect); | 984 | maskRect = maskRect.intersected(imageRect); | ||
1000 | 985 | | |||
1001 | KisPaintDeviceSP tempImageDev = new KisPaintDevice(imageDev->colorSpace()); | | |||
1002 | KisPaintDeviceSP tempMaskDev = new KisPaintDevice(maskDev->colorSpace()); | | |||
1003 | tempImageDev->makeCloneFrom(imageDev, maskRect); | | |||
1004 | tempMaskDev->makeCloneFrom(maskDev, maskRect); | | |||
1005 | | ||||
1006 | if (!maskRect.isEmpty()) { | 986 | if (!maskRect.isEmpty()) { | ||
1007 | Inpaint inpaint(tempImageDev, tempMaskDev, patchRadius); | 987 | Inpaint inpaint(imageDev, maskDev, patchRadius, maskRect); | ||
1008 | MaskedImageSP output = inpaint.patch(); | 988 | MaskedImageSP output = inpaint.patch(); | ||
1009 | output->toPaintDevice(imageDev, maskRect); | 989 | output->toPaintDevice(imageDev, maskRect); | ||
1010 | } | 990 | } | ||
1011 | 991 | | |||
1012 | return maskRect; | 992 | return maskRect; | ||
1013 | } | 993 | } | ||
1014 | 994 | |
Does this mean the inpainting tool should be disabled for now when the color depth is other than 8 bits?