I am trying to learn python by doing some small projects. one of them is as follows:
I want to write a code that converts an image to straight lines. I am actually into string art and want to receive the output in a way that can be used to easily build the string art by using the output of my code.
so, I'll try to explain what I'm trying to do:
1. import an image and get the pixel coordinate and save them into an array.
2.get brightness value for each pixel of the image.
3. choose a number of lines that are going to be the "quality" of my output, in reality, they'd be the strings that I use to create the art.
4.draw a random amount of lines through the darkest pixel of the image, and compare each lines total brightness to the others, the darkest (the line hitting the darkest pixels) is chosen and the pixels in that line are removed from the pixel array.
5.save the two x and y coordinate of every line's intersection with the image borders ( my canvas). so I can use this later and know where to start and finish ny strings.
6. repeat the step 4 and 5, for the number of lines chosen.
7. print or save the XY coordinates of the line intersections with image borders.
I plan using PIL and numpy to do this, now my question is this:
a. do you think there are easier or better ways to active my goal?
b. what is the best way to get a clean array of pixels from any given digital image?
you can see the kind of image I'm trying to produce at linify.me
thanks.
Related
If i'm very unclear, what I mean is the following:
I have a text file, and in the text file, there are a bunch of lines that are rgb color values. And then the program will do the following:
On line one on the txt file, it will tell the program how big the file will be (as in resolution, not in gigabytes and stuff like that). On line 2 it would fill the first pixel of an image (the one in the top left corner) with the rgb value on line one on the text file. On line 3, it would fill the pixel below pixel 1. On line 4 it would fill the pixel below pixel 2. On line 5, so on and so forth. In the end, the program will make some kind of image with all of those pixels.
Now, my question is: is this applicable in pygame? Like, with all of those pixel places, you can then make a surface for a sprite? All help is appreciated!
This is possible to do.
You can create a pygame.Surface with the dimensions given, then use the values of the other lines in a nested for loop, setting pixels using surface.set_at((x, y), color) (https://www.pygame.org/docs/ref/surface.html#pygame.Surface.set_at)
This will be slow, though, especially for larger surfaces
is this applicable in pygame?
I have no idea why you would do it like this. pygame.image.load() supports many popular image formats. If you want something that can be loaded and saved, pygame.image.save() also exists and works.
If you add anything about what exact problem you're trying to solve by doing this, maybe I can suggest something more specific.
I am trying to find or think of an algorithm that finds a path from a thick line. I think the images make easier to understand what I am trying to do.
Given is a 2D array as the picture with values 0 and 1 and I am trying to find the nodes of the lines. Has anybody an idea?
You could follow the contour and nibble away pixel by pixel (checking that the connectivity stays intact).
If you cannot remove any more pixels, you have a 1 pixel line as wanted.
But the line will most likely have very few long linear segments (unlike in your example)
I recommend using the most famous python library for image treatment : Pillow.
https://python-pillow.org/
Some questions to orientate you :
Is it really black and white source image ? If No, the first step will be to make each pixel of this picture either black or white (pillow proposes this feature)
Is the width of the white pattern constant (i.e. always 15 pixels) ? If No, your program will first need to scan first the whole picture and then to guess the pattern. If Yes, you can guess the pattern while scanning the picture.
But what means "scanning the picture" ?
That's the key question.
You could check all lines of pixels (from the first line till the last line, and for each line, from left to right), each time you encounter a white pixel you record its coordinates and you record how many white pixels are aside this first white pixel.
Doing that, you will get a table where all white pixels are located.
Then, it's more about mathemtics than about programming.
I am making an application which is supposed to detect gray digits on an either a black background with varying tones or rgb(184, 180, 152).
Example date:
Example digit image which is cut from the menu 4x8 with only grey color:
While the montone background allowed for a simple Image.open(...).tobytes.index(Image.open(<filepath to digit>).tobytes()) to check if given digit was in the image, since every background remains the same, the "varying" background has a different color pattern for each digit's non-gray pixels in the 4x8 space. Getting a seperate file for each line's all 10 digits would take so much time and most likely render my application very slow.
The irregular return values provided by .tobytes() method made it practically impossible (to my knowledge at least) to apply a RegEx to fill the blank pixels with any arbitrary color and that way checking solely the gray areas.
My desired converted data "format" of the images would be one in which each pixel corresponds to its own sequence of characthers.
For example a color corresponding to blank, blank, gray would become 000000001040 once converted from its Image.open(...)-form where 0000is the "blank". That way I could apply a RegEx from the returned string which would fill the blanks with any arbitrary color. That way only the grey will be check as opposed to also controlling non-gray colors inside the 4x8 area. What I get using .tobytes() rather corresponds to 1123999938212; the pattern seems to change whenever a pixel is added and therefore unpredictable.
However, I suspect that this is the fault of tobytes() and that binary can fullfill my need of obtaining a sequence representing each pixel. If this is right, how can I convert the image to a binary sequence to use as a string?
Note: When I say "format" I am not meaning extention, instead data "formats" such as binary, hex or XML.
I am hestiant to use OpenCV since it might be an overkill that will make the application- which will scan through several images- slower. In lack of a better solution I will opt for it.
As it turned out, PIL already provides a method perfect for my need.
The entire image can be converted to a list of tuples containing each respective pixel's RGBA using: list((Image.open(...).getdata()), where (255,255,255, 0) is a blank pixel.
Given an image of a connect-4 board I'd like to recognize and output the board's state (a 6 by 7 matrix). The first approach I tried was based on finding the circles and then looking for a grid pattern in their centroids.
This is the open-cv function I'm using:
circles = cv2.HoughCircles(bw_im,
cv2.cv.CV_HOUGH_GRADIENT,
dp=DP,
minDist=MIN_DIST,
minRadius=MIN_RADIUS,
maxRadius=MAX_RADIUS)
I add non-maximum suppression, but the results are not great.
Is there a better way than dealing with Hough circles directly, perhaps there is some sort of filled circularity morphological operation that I don't know of.
Here's an example input image:
You can assume that the input image has been cropped and has similar margins as above (I have another piece of code that takes care of this).
If Hough isn't a requirement, Id suggest implementing a ray-casting algorithm as described here: https://en.wikipedia.org/wiki/Point_in_polygon
The general steps are:
Create a mask for the red circles
Run ray-casting on x columns spaced y apart to determine # and position of reds
Repeat steps 1 & 2 for yellow
Since you're working in RGB, the color contrast should be enough to give you good results.
Assuming your grid will maintain its position the easiest way would be to setup a fixed region of interest for every slot and measure their hue values every time you change something.
I have about 3000 images and 13 different colors (the background of the majority of these images is white). If the main color of an image is one of those 13 different colors, I'd like them to be associated.
I've seen similar questions like Image color detection using python that ask for an average color algorithm. I've pretty much copied that code, using the Python Image Library and histograms, and gotten it to work - but I find that it's not too reliable for determining main colors.
Any ideas? Or libraries that could address this?
Thanks in advance!
:EDIT:
Thanks guys - you all pretty much said the same thing, to create "buckets" and increase the bucket count with each nearest pixel of the image. I seem to be getting a lot of images returning "White" or "Beige," which is also the background on most of these images. Is there a way to work around or ignore the background?
Thanks again.
You can use the getcolors function to get a list of all colors in the image. It returns a list of tuples in the form:
(N, COLOR)
where N is the number of times the color COLOR occurs in the image. To get the maximum occurring color, you can pass the list to the max function:
>>> from PIL import Image
>>> im = Image.open("test.jpg")
>>> max(im.getcolors(im.size[0]*im.size[1]))
(183, (255, 79, 79))
Note that I passed im.size[0]*im.size[1] to the getcolors function because that is the maximum maxcolors value (see the docs for details).
Personally I would split the color space into 8-16 main colors, then for each pixel I'd increment the closest colored bucket by one. At the end the color of the bucket with the highest amount of pixels wins.
Basically, think median instead of average. You only really care about the colors in the image, whereas averaging colors usually gives you a whole new color.
Since you're trying to match a small number of preexisting colors, you can try a different approach. Test each image against all of the colors, and see which one is the closest match.
As for doing the match, I'd start by resizing each image to a smaller size to reduce the amount of work you'll be doing for each; our perception of the color of an image isn't too dependent on the amount of detail. For each pixel of the smaller image, find which of the 13 colors is the closest. If it's within some threshold, bump a counter for that color. At the end whichever of the 13 has the highest count is the winner.