Filter on CALayer except for a shape which is an union of (non necessarily distinct) rectangles
AFAIK, you can't mask a layer to the inverse of a path.
A couple of observations:
If you were just trying to knock out the paths inside the whole view, you could do that with the trick of creating a path that consists of the whole
CGRect
plus the various interior paths and leverage the even/odd winding/fill rule, but that won't work if your interior paths overlap with each other.You can mask images to the inverse of a path (by creating a separate "image mask"), but that won't work for dynamic
CALayer
masking. It's used for masking aNSImage
. So, if you were OK using a snapshot for the filtered part of the view, that's an option.See code snippet below for example of using image masks.
Another approach is to apply your filter to the whole view, snapshot it, put that snapshot underneath the view in question and then mask the top level view to your interior paths. In effect, mask the un-filtered view to your interior paths, revealing a filtered snapshot of your view below it.
Yet approach would be to create a path representing the outline of the union of all of your interior paths. If the paths are simple (e.g. non-rotated rectangles) this is pretty easy. If the paths are complex (some rotated, some non-rectangular paths, etc.), this gets hairy. But the trivial scenario isn't too bad. Anyway, if you do that, then you can fall back to that even-odd trick.
I'm not wholly satisfied with any of these approaches, but I don't see any other way to accomplish what you're looking for. Hopefully someone will suggest some better ways to tackle this.
To expand on option 2 (using an image mask created by drawing a few paths, possibly overlapping), in Swift 3 you can do something like:
private func maskImageWithPaths(image: NSImage, size: CGSize) -> NSImage {
// define parameters for two overlapping paths
let center1 = CGPoint(x: size.width * 0.40, y: size.height / 2)
let radius1 = size.width * 0.20
let center2 = CGPoint(x: size.width * 0.60 , y: size.height / 2)
let radius2 = size.width * 0.20
// create these two overlapping paths
let path = CGMutablePath()
path.move(to: center1)
path.addArc(center: center1, radius: radius1, startAngle: -.pi / 2, endAngle: .pi * 3 / 2, clockwise: false)
path.move(to: center2)
path.addArc(center: center2, radius: radius2, startAngle: -.pi / 2, endAngle: .pi * 3 / 2, clockwise: false)
// create image from these paths
let imageRep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSDeviceRGBColorSpace, bitmapFormat: .alphaFirst, bytesPerRow: 4 * Int(size.width), bitsPerPixel: 32)!
let context = NSGraphicsContext(bitmapImageRep: imageRep)!
context.cgContext.addPath(path)
context.cgContext.setFillColor(NSColor.blue.cgColor)
context.cgContext.fillPath()
let maskImage = context.cgContext.makeImage()!
let mask = CGImage(maskWidth: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: 4 * Int(size.width), provider: maskImage.dataProvider!, decode: nil, shouldInterpolate: true)!
let finalImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil)!.masking(mask)!
return NSImage(cgImage: finalImage, size: size)
}
That yields:
This masks out the drawn paths.
macOS draw a rectangle with two holes inside
You don't have to use Core Graphics. You can create a NSView
subclass and just stroke
/fill
the path in draw(_:)
. In Swift 3:
class HolyView: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let path = ... // build the `NSBezierPath` however you want
NSColor.blue.setFill()
path.fill()
}
}
You can then add that view programmatically, or you can make it @IBDesignable
and add it directly on your storyboard.
Get bounds of filters applied to Flash Sprite within Sprite
Oh sweet success! (and thanks for the tips) A friend helped solve the problem with a nice recursive function to handle the filters which may exist on nested sprites:
private function getDisplayObjectRectangle(container:DisplayObjectContainer, processFilters:Boolean):Rectangle {
var final_rectangle:Rectangle = processDisplayObjectContainer(container, processFilters);
// translate to local
var local_point:Point = container.globalToLocal(new Point(final_rectangle.x, final_rectangle.y));
final_rectangle = new Rectangle(local_point.x, local_point.y, final_rectangle.width, final_rectangle.height);
return final_rectangle;
}
private function processDisplayObjectContainer(container:DisplayObjectContainer, processFilters:Boolean):Rectangle {
var result_rectangle:Rectangle = null;
// Process if container exists
if (container != null) {
var index:int = 0;
var displayObject:DisplayObject;
// Process each child DisplayObject
for(var childIndex:int = 0; childIndex < container.numChildren; childIndex++){
displayObject = container.getChildAt(childIndex);
//If we are recursing all children, we also get the rectangle of children within these children.
if (displayObject is DisplayObjectContainer) {
// Let's drill into the structure till we find the deepest DisplayObject
var displayObject_rectangle:Rectangle = processDisplayObjectContainer(displayObject as DisplayObjectContainer, processFilters);
// Now, stepping out, uniting the result creates a rectangle that surrounds siblings
if (result_rectangle == null) {
result_rectangle = displayObject_rectangle.clone();
} else {
result_rectangle = result_rectangle.union(displayObject_rectangle);
}
}
}
// Get bounds of current container, at this point we're stepping out of the nested DisplayObjects
var container_rectangle:Rectangle = container.getBounds(container.stage);
if (result_rectangle == null) {
result_rectangle = container_rectangle.clone();
} else {
result_rectangle = result_rectangle.union(container_rectangle);
}
// Include all filters if requested and they exist
if ((processFilters == true) && (container.filters.length > 0)) {
var filterGenerater_rectangle:Rectangle = new Rectangle(0,0,result_rectangle.width, result_rectangle.height);
var bmd:BitmapData = new BitmapData(result_rectangle.width, result_rectangle.height, true, 0x00000000);
var filter_minimumX:Number = 0;
var filter_minimumY:Number = 0;
var filtersLength:int = container.filters.length;
for (var filtersIndex:int = 0; filtersIndex < filtersLength; filtersIndex++) {
var filter:BitmapFilter = container.filters[filtersIndex];
var filter_rectangle:Rectangle = bmd.generateFilterRect(filterGenerater_rectangle, filter);
filter_minimumX = filter_minimumX + filter_rectangle.x;
filter_minimumY = filter_minimumY + filter_rectangle.y;
filterGenerater_rectangle = filter_rectangle.clone();
filterGenerater_rectangle.x = 0;
filterGenerater_rectangle.y = 0;
bmd = new BitmapData(filterGenerater_rectangle.width, filterGenerater_rectangle.height, true, 0x00000000);
}
// Reposition filter_rectangle back to global coordinates
filter_rectangle.x = result_rectangle.x + filter_minimumX;
filter_rectangle.y = result_rectangle.y + filter_minimumY;
result_rectangle = filter_rectangle.clone();
}
} else {
throw new Error("No displayobject was passed as an argument");
}
return result_rectangle;
}
Detect Door Shape in Floor plan using C#
Sorry for the python code. But perhaps this will help solve your problem.
See comments.
import cv2
img = cv2.imread('NHoXn.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# convert to binary image
thresh=cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY )[1]
# Morphological reconstruction (delete labels)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
marker = cv2.dilate(thresh,kernel,iterations = 1)
while True:
tmp=marker.copy()
marker=cv2.erode(marker, kernel2)
marker=cv2.max(thresh, marker)
difference = cv2.subtract(tmp, marker)
if cv2.countNonZero(difference) == 0:
break
# only walls
se=cv2.getStructuringElement(cv2.MORPH_RECT, (4,4))
walls=cv2.morphologyEx(marker, cv2.MORPH_CLOSE, se)
walls=cv2.erode(walls, kernel2,iterations=2)
# other objects
other=cv2.compare(marker,walls, cv2.CMP_GE)
other=cv2.bitwise_not(other)
# find connected components and select by size and area
output = cv2.connectedComponentsWithStats(other, 4, cv2.CV_32S)
num_labels = output[0]
labels = output[1]
stats=output[2]
centroids = output[3]
for i in range(num_labels):
left,top,width,height,area=stats[i]
if abs(width-40)<12 and abs(height-40)<12 and area>85:
cv2.rectangle(img,(left, top), (left+width, top+height), (0,255,0))
cv2.imwrite('doors.png', img)
Result:
- What is shown in the drawing: walls, doors, windows, furniture, text labels.
- The doors to be found always touch the walls.
- How are walls different from other objects? Thick, these lines are bold. Thus, dilatation with the desired structural element can leave only parts of the walls. And then, by morphological reconstruction, restore the walls together with the elements that concern them: doors, windows in the first place. The drawing will be cleaned of everything that does not touch the walls.
- If dilatation and then erosion are further done, then only walls will remain, thin elements, like windows and doors will disappear.
- Subtracting (or logical operations) from the third stage the fourth we get a picture that contains only doors, windows and furniture that touched the walls.
- What is the difference in the drawing of the door from the windows? The fact that their BB is almost square, the size is approximately the same for all doors in this drawing, their length is approximately equal to r*(1+pi/4).
Further in the code there is a selection for such signs. At this stage, you can add some more signs that will more accurately separate the doors from other elements.
Related Topics
Detecting Swipes on All Four Directions on Watchkit Using The Storyboard
How to Draw a Line Between Two Points Over an Image in Swift 3
Shorthand for Wrapping a Swift Variable in an Optional
How to Calculate Quadrangle for Visible Part of Vertical Plane
Combine Sink: Ignore Receivevalue, Only Completion Is Needed
Swift 4 - Hmcharacteristictypeserialnumber Deprecated
How to Present a UIcollectionview in Swiftui with UIviewcontrollerrepresentable
Override UIgesturerecognizer Touchesbegan
Conversion Between Cgfloat and Nsnumber Without Unnecessary Promotion to Double
Bad_Access During Recursive Calls in Swift
Swift 2.0 Replicate Objc_Association_Retain
Swift System Version Checking on Ubuntu
Swift Protocol Extension Implementing Another Protocol with Shared Associated Type
Swift: Binary Operator '==' Cannot Be Applied to Operands of Type "Protocol"
How to Distinguish Bool and Int in Swift
Create an Array of Protocols with Constrained Associated Types