PixelNeighbors::PixelNeighbors(int width, int height, const Mask& mask, Mask::Value maskValidNeighborValue, int x, int y) : m_hasTop(y > 0 && mask.GetValue(x, y - 1) == maskValidNeighborValue) , m_hasBottom(y < height - 1 && mask.GetValue(x, y + 1) == maskValidNeighborValue) , m_hasLeft(x > 0 && mask.GetValue(x - 1, y) == maskValidNeighborValue) , m_hasRight(x < width - 1 && mask.GetValue(x + 1, y) == maskValidNeighborValue) { CacheNum(); }
void AAndBVars::Evaluate(const ImageFloat& inputImage, const Mask& mask, const UnknownVars& unknownVars, int x, int y, PixelFloat& inOutPixel, const unsigned int variableId) { wxASSERT(x >= 0); wxASSERT(y >= 0); wxASSERT(x < inputImage.GetWidth()); wxASSERT(y < inputImage.GetHeight()); if (mask.GetValue(x, y) == Mask::UNKNOWN) { // Unknown pixel for (int component = 0; component < LfnIc::PixelFloat::NUM_CHANNELS; ++component) { m_a.insert(variableId + unknownVars.GetNum() * component, unknownVars.GetIndex(x,y) + unknownVars.GetNum() * component) = 1.0f; } } else { // Known pixel inOutPixel -= inputImage.GetPixel(x, y); } }
UnknownVars::UnknownVars(int width, int height, const Mask& mask) : m_width(width) , m_height(height) { wxASSERT(m_width <= USHRT_MAX); wxASSERT(m_height <= USHRT_MAX); int i = 0; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { if (mask.GetValue(x, y) == Mask::UNKNOWN) { m_xyToUnknownIndexMap[GetKeyFromXy(x, y)] = i++; } } } m_u.resize(GetNum() * LfnIc::PixelFloat::NUM_CHANNELS, 1); }
AAndBVars::AAndBVars(const ImageFloat& inputImage, const Mask& mask, const BVarInitializer& bVarInitializer, const UnknownVars& unknownVars) { // The A matrix is 3N * 3N where N is the number of masked pixels m_a.resize(unknownVars.GetNum() * LfnIc::PixelFloat::NUM_CHANNELS, unknownVars.GetNum() * LfnIc::PixelFloat::NUM_CHANNELS); // The 'b' values m_b.resize(unknownVars.GetNum() * LfnIc::PixelFloat::NUM_CHANNELS, 1); const int width = inputImage.GetWidth(); const int height = inputImage.GetHeight(); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { if (mask.GetValue(x, y) == Mask::UNKNOWN) { const PixelNeighbors neighbors(width, height, x, y); PixelFloat pixel; bVarInitializer.GetInitialValue(x, y, pixel); const int unknownIndex = unknownVars.GetIndex(x, y); Evaluate(neighbors.HasTop(), inputImage, mask, unknownVars, x, y - 1, pixel, unknownIndex); Evaluate(neighbors.HasLeft(), inputImage, mask, unknownVars, x - 1, y, pixel, unknownIndex); Evaluate(neighbors.HasRight(), inputImage, mask, unknownVars, x + 1, y, pixel, unknownIndex); Evaluate(neighbors.HasBottom(), inputImage, mask, unknownVars, x, y + 1, pixel, unknownIndex); // This is the center pixel, so the corresponding entry should be on the diagonal for (int component = 0; component < LfnIc::PixelFloat::NUM_CHANNELS; ++component) { m_a.insert(unknownIndex + unknownVars.GetNum() * component, unknownIndex + unknownVars.GetNum() * component) = -neighbors.GetNum(); m_b(unknownIndex + unknownVars.GetNum() * component) = pixel.channel[component]; } } } } }
void WriteOutput( const UnknownVars& unknownVars, const Mask& mask, ImageFloat& inputOutputImage, int translateSolutionX, int translateSolutionY) { const int srcWidth = unknownVars.GetWidth(); const int srcHeight = unknownVars.GetHeight(); const int outputWidth = inputOutputImage.GetWidth(); const int outputHeight = inputOutputImage.GetHeight(); const int numUnknownVars = unknownVars.GetNum(); // Convert solution vector back to image for (int y = 0; y < outputHeight; ++y) { for (int x = 0; x < outputWidth; ++x) { const int srcX = x - translateSolutionX; const int srcY = y - translateSolutionY; if (srcX >= 0 && srcY >= 0 && srcX < srcWidth && srcY < srcHeight) { if (mask.GetValue(srcX, srcY) == Mask::UNKNOWN) { const int i = unknownVars.GetIndex(srcX, srcY); PixelFloat p; for (int component = 0; component < PixelFloat::NUM_CHANNELS; ++component) { p.channel[component] = unknownVars.GetFloatClamped0To1(i + numUnknownVars * component); } inputOutputImage.SetPixel(x, y, p); } } } } }
void Cloner::UpdateLaplacian( const ImageFloat& sourceImage, const Mask& mask, int sourceOffsetX, int sourceOffsetY) { const int sourceWidth = sourceImage.GetWidth(); const int sourceHeight = sourceImage.GetHeight(); const int sourceImageTranslatedWidth = m_sourceImageTranslated.GetWidth(); const int sourceImageTranslatedHeight = m_sourceImageTranslated.GetHeight(); wxASSERT(sourceImageTranslatedWidth == m_laplacian.GetWidth()); wxASSERT(sourceImageTranslatedHeight == m_laplacian.GetHeight()); // Reset m_laplacianMask to all Mask::KNOWN. { for (int y = m_sourceImageTranslatedRect.y, ny = y + m_sourceImageTranslatedRect.height; y < ny; ++y) { for (int x = m_sourceImageTranslatedRect.x, nx = x + m_sourceImageTranslatedRect.width; x < nx; ++x) { m_laplacianMask.SetValue(x, y, Mask::KNOWN); } } #if _DEBUG for (int y = 0; y < sourceImageTranslatedHeight; ++y) { for (int x = 0; x < sourceImageTranslatedWidth; ++x) { wxASSERT(m_laplacianMask.GetValue(x, y) == Mask::KNOWN); } } #endif } // Set m_sourceImageTranslatedRect { m_sourceImageTranslatedRect = wxRect(sourceOffsetX, sourceOffsetY, sourceWidth, sourceHeight); m_sourceImageTranslatedRect.Intersect(wxRect(0, 0, sourceImageTranslatedWidth, sourceImageTranslatedHeight)); } // Copy the source into m_sourceImageTranslated at the source offset, // and translate the source mask into m_laplacianMask. for (int y = 0; y < sourceImageTranslatedHeight; ++y) { for (int x = 0; x < sourceImageTranslatedWidth; ++x) { const int translatedX = x - sourceOffsetX; const int translatedY = y - sourceOffsetY; if (translatedX >= 0 && translatedY >= 0 && translatedX < sourceWidth && translatedY < sourceHeight) { const int sourceIndex = LfnTech::GetRowMajorIndex(sourceWidth, translatedX, translatedY); const int translatedIndex = LfnTech::GetRowMajorIndex(sourceImageTranslatedWidth, x, y); m_sourceImageTranslated.GetData()[translatedIndex] = sourceImage.GetData()[sourceIndex]; m_laplacianMask.SetValue(x, y, mask.GetValue(translatedX, translatedY)); } } } // Compute the laplacian from the translated source image. for (int y = 0; y < sourceImageTranslatedHeight; ++y) { for (int x = 0; x < sourceImageTranslatedWidth; ++x) { // Only interested in computing the laplacian of the unknown // pixels from the translated source. const PixelNeighbors neighbors(sourceImageTranslatedWidth, sourceImageTranslatedHeight, m_laplacianMask, Mask::UNKNOWN, x, y); if (m_laplacianMask.GetValue(x, y) == Mask::UNKNOWN || neighbors.GetNum() > 0) { PixelFloat rgb = m_sourceImageTranslated.GetPixel(x, y); rgb *= float(-neighbors.GetNum()); if (neighbors.HasLeft()) { rgb += m_sourceImageTranslated.GetPixel(x - 1, y); } if (neighbors.HasRight()) { rgb += m_sourceImageTranslated.GetPixel(x + 1, y); } if (neighbors.HasTop()) { rgb += m_sourceImageTranslated.GetPixel(x, y - 1); } if (neighbors.HasBottom()) { rgb += m_sourceImageTranslated.GetPixel(x, y + 1); } m_laplacian.SetPixel(x, y, rgb); } } } }