# need to find red zones out of 9 zones in the img from PIL import Image, ImageFont, ImageDraw, features import math def getRedZones(imgPath, isCmap2=False): img = Image.open(imgPath) pixelValues = list(img.getdata()) colors = img.getcolors() print("colors: ", colors) print("totalPixels: ", len(pixelValues)) # print("pixelValues: ", pixelValues[:1000]) # for i in range(len(pixelValues)): # pixelData = pixelValues[i] # if pixelData[3] == 255: # print(i, pixelData) # return # return imgSize = img.size # w-h print("imgSize: ", imgSize) imgWidth = imgSize[0] imgHeight = imgSize[1] zoneWidth = imgWidth/3 zoneHeight = imgHeight/3 redZoneMatrix = {} fieldAreaMatrix = {} for i in range(9): addZoneToRawMatixIfNone(redZoneMatrix, i+1) fieldAreaMatrix[i+1] = 0 for x in range(imgWidth): for y in range(imgHeight): zoneCol = math.ceil((x+1)/zoneWidth) zoneRow = math.ceil((y+1)/zoneHeight) zoneNo = (zoneRow-1)*3 + zoneCol # assuming it to be tuple of rgba pixelData = pixelValues[y*imgWidth + x] isFieldPixel = pixelData[3] == 255 if isFieldPixel: if zoneNo not in fieldAreaMatrix: fieldAreaMatrix[zoneNo] = 0 fieldAreaMatrix[zoneNo] += 1 isRedZonePixel = findIfRedZonePixel(pixelData, isCmap2) if isRedZonePixel: isLeftInZone = getIsLeftZone( zoneNo, x, y, imgWidth, imgHeight) # addZoneToRawMatixIfNone(redZoneRawMatrix, zoneNo) redZoneMatrix[zoneNo]['total'] += 1 redZoneMatrix[zoneNo]['left' if isLeftInZone else 'right'] += 1 print("redZones: ", redZoneMatrix) print("zoneAreas: ", fieldAreaMatrix) # get redZoneMatrix from raw totalFieldArea = sum(fieldAreaMatrix.values()) print("zoneAreaSum: ", totalFieldArea) minArea = 0.02*totalFieldArea for zoneNo, redCount in redZoneMatrix.items(): if zoneNo != 5 and (fieldAreaMatrix[zoneNo] or 0) < minArea: # shift this zone's redCount to adjecent zones leftZoneNo = getAdjecentZoneNo( zoneNo, True, fieldAreaMatrix, minArea) # addZoneToRawMatixIfNone(redZoneRawMatrix, leftZoneNo, fieldAreaMatrix, minArea) redZoneMatrix[leftZoneNo]["total"] += redCount["left"] redZoneMatrix[leftZoneNo]["right"] += redCount["left"] rightZoneNo = getAdjecentZoneNo( zoneNo, False, fieldAreaMatrix, minArea) # addZoneToRawMatixIfNone(redZoneRawMatrix, rightZoneNo, fieldAreaMatrix, minArea) redZoneMatrix[rightZoneNo]["total"] += redCount["right"] redZoneMatrix[rightZoneNo]["right"] += redCount["right"] redZoneMatrix[zoneNo]["total"] = 0 redZoneMatrix[zoneNo]["right"] = 0 redZoneMatrix[zoneNo]["left"] = 0 print(zoneNo, leftZoneNo, rightZoneNo) print("totalRedArea: ", sum(map(returnZoneTotal, redZoneMatrix.values()))) redZones = [] print(redZoneMatrix) for i in range(9): if redZoneMatrix[i+1]["total"] > 0.15*fieldAreaMatrix[i+1]: redZones.append(i+1) return redZones def returnZoneTotal(zoneData): return zoneData["total"] zoneOrder = [1, 2, 3, 6, 9, 8, 7, 4] zones = [1, 2, 3, 4, 5, 6, 7, 8, 9] def getAdjecentZoneNo(zoneNo, isLeft, areaMatrix, minArea): # print(zoneNo, isLeft) zoneIndex = zoneOrder.index(zoneNo) zone = zoneOrder[(zoneIndex+(-1 if isLeft else 1)) % 8] if (areaMatrix[zone] or 0) < minArea: zone = getAdjecentZoneNo(zone, isLeft, areaMatrix, minArea) return zone def getIsLeftZone(zoneNo, x, y, imgWidth, imgHeight): if zoneNo == 2: return x < imgWidth/2 if zoneNo == 6: return y < imgHeight/2 if zoneNo == 8: return x > imgWidth/2 if zoneNo == 4: return y > imgHeight/2 if zoneNo == 1: return y > x*imgHeight/imgWidth if zoneNo == 3: return y < (imgWidth-x)*imgHeight/imgWidth if zoneNo == 9: return (imgHeight-y) > (imgWidth-x)*imgHeight/imgWidth if zoneNo == 7: return (imgHeight-y) < x*imgHeight/imgWidth def addZoneToRawMatixIfNone(matrix, zoneNo): if zoneNo not in matrix: matrix[zoneNo] = {'total': 0, 'left': 0, 'right': 0} # (255, 240, 179, 255), redPixels = [(251, 193, 126, 255), (247, 137, 89, 255), (234, 77, 57, 255), (173, 5, 53, 255)] redPixels2 = [(13, 13, 13, 255), (191, 191, 191, 255), (219, 219, 219, 255), (235, 235, 235, 255), (254, 250, 202, 255), (238, 232, 182, 255), (222, 218, 157, 255), (205, 200, 132, 255), (191, 183, 108, 255), (175, 195, 98, 255), (164, 205, 87, 255)] # elseif ind_i>0.15 && ind_i <= 0.175 # c_map2(i,1) = 164; # c_map2(i,2) = 205; # c_map2(i,3) = 87; # elseif ind_i>0.175 && ind_i <= 0.2 # c_map2(i,1) = 127; # c_map2(i,2) = 179; # c_map2(i,3) = 71; # elseif ind_i>0.2 && ind_i <= 0.25 # c_map2(i,1) = 111; # c_map2(i,2) = 161; # c_map2(i,3) = 64; def findIfRedZonePixel(pixelData, isCmap2): return checkIfColorInList(pixelData, (redPixels if not isCmap2 else redPixels2)) for redPixelData in (redPixels if not isCmap2 else redPixels2): if abs(sum(redPixelData)-sum(pixelData)) < 10: return True return False def checkIfColorInList(color, list): for pixelData in list: if abs(sum(pixelData)-sum(color)) < 10: return True return False def getInstructionsByZones(redZones, translations, isIrri, lang): if lang in ["hi", "en"]: irriZones = redZones if isIrri else [] cropZones = [] if isIrri else redZones templateCode = 1 if len(redZones) == 0 else 0 template = "%s%s" % (templateCode if not isIrri else "_", templateCode if isIrri else "_") return getSmsFullInstructionsByZones(template, irriZones, cropZones, translations, lang) result = "%s- %s" % (translations['forCropProbInstruc' if not isIrri else 'forIrriProbInstruc'], ", ".join(map(zoneToStr, redZones))) return result def getFullDirByCode(code): if code == "N": return "north" if code == "W": return "west" if code == "E": return "east" if code == "C": return "ceNter" if code == "S": return "south" r2lLangs = ["ar"] def getLocalDirByZone(zone, translations, langCode, addNewLine=True): newLineTxt = "\n" if addNewLine else " " if zone == 1: if langCode in r2lLangs: return translations[getFullDirByCode("W")]+newLineTxt+translations[getFullDirByCode("N")] return translations[getFullDirByCode("N")]+newLineTxt+translations[getFullDirByCode("W")] if zone == 2: return translations[getFullDirByCode("N")] if zone == 3: if langCode in r2lLangs: return translations[getFullDirByCode("E")]+newLineTxt+translations[getFullDirByCode("N")] return translations[getFullDirByCode("N")]+newLineTxt+translations[getFullDirByCode("E")] if zone == 4: return translations[getFullDirByCode("W")] if zone == 5: return translations[getFullDirByCode("C")] if zone == 6: return translations[getFullDirByCode("E")] if zone == 7: if langCode in r2lLangs: return translations[getFullDirByCode("W")]+newLineTxt+translations[getFullDirByCode("S")] return translations[getFullDirByCode("S")]+newLineTxt+translations[getFullDirByCode("W")] if zone == 8: return translations[getFullDirByCode("S")] if zone == 9: if langCode in r2lLangs: return translations[getFullDirByCode("E")]+newLineTxt+translations[getFullDirByCode("S")] return translations[getFullDirByCode("S")]+newLineTxt+translations[getFullDirByCode("E")] def getSmsInstructionsByZones(redZones, translations, langCode): def getLocalDir(zone): return getLocalDirByZone(zone, translations, langCode, False) zoneList = list(map(getLocalDir, redZones)) # print(zoneList) result = ", ".join(zoneList) return result smsTrans = { "hi": { "sms00": "सिचाई जांच की आवश्यकता {1} और फसल जांच की आवश्यकता {2} है|", "sms10": "फसल जांच की आवश्यकता {2} है और सिचाई अच्छी स्तिथि में है|", "sms01": "सिचाई जांच की आवश्यकता {1} है और फसल अच्छी स्तिथि में है|", "sms11": "सिचाई और फसल दोनों अच्छी स्तिथि में है।", "sms_1": "सिचाई अच्छी स्तिथि में है।", "sms1_": "फसल अच्छी स्तिथि में है।", "sms_0": "सिचाई जांच की आवश्यकता {1} है।", "sms0_": "फसल जांच की आवश्यकता {2} है।", "smsMain": "{1}{2}", "smsOther": "{1} के अलावा{2}", "smsNum2": " दिशाओं में", "smsMulti": "", "smsNumMax": 2, "smsAll": "पुरे खेत में", }, "en": { "sms00": "irrigation inspection is required in {1} and crop inspection is required in {2} of your field.", "sms01": "irrigation inspection is required in {1} of your field and crop is in good condition.", "sms10": "crop inspection is required in {2} of your field and irrigation is in good condition.", "sms11": "both irrigation and crop are in good condition.", "sms_1": "Irop are in good condition.", "sms1_": "Crop are in good condition.", "sms_0": "Irrigation inspection is required in {1} of your field.", "sms0_": "Crop inspection is required in {2} of your field.", "smsMain": "{1} direction{2}", "smsOther": "directions other than {1}", "smsNum2": "", "smsNum2_5": "s", "smsMulti": "2", "smsNumMax": 2, "smsAll": "all directions", } } def saveTxtToFile(txt, outFile="tmpTxtFile.txt"): with open(outFile, 'w', encoding="utf8") as f: f.write(txt) def replaceDirsIn(zones, translations, langCode, isMain=True): # expects langCode in ["hi", "en"] langTrans = translations #smsTrans[langCode] resultStr = langTrans["sms%s" % ("All" if len( zones) == 9 else "Main" if isMain else "Other")] # print(resultStr) resultStr = resultStr.replace( "{1}", getSmsInstructionsByZones(zones, translations, langCode)) # print(resultStr) multiNums = langTrans["smsMulti"].split(",") numMax = langTrans["smsNumMax"] # print(numMax, multiNums) index = 2 while(index <= numMax): indexKey = "smsNum%s" % (index) # print(index, indexKey) if str(index) in multiNums: if len(zones) == 1: resultStr = resultStr.replace( "{%s}" % (index), langTrans[indexKey]) else: resultStr = resultStr.replace( "{%s}" % (index), langTrans[indexKey+"_5"]) else: resultStr = resultStr.replace( "{%s}" % (index), langTrans[indexKey]) index += 1 # print(resultStr) return resultStr def getSmsFullInstructionsByZones(templateType, irriZones, cropZones, translations, langCode): # expects only for hi; expects zones to be non-None irri2Zones = [x for x in zones if x not in irriZones] crop2Zones = [x for x in zones if x not in cropZones] if langCode in ["hi", "en"]: irriDirsStr = replaceDirsIn(irriZones, translations, langCode) irri2DirsStr = replaceDirsIn(irri2Zones, translations, langCode, False) cropDirsStr = replaceDirsIn(cropZones, translations, langCode) crop2DirsStr = replaceDirsIn(crop2Zones, translations, langCode, False) else: irri2DirsStr = "" irriDirsStr = "" crop2DirsStr = "" cropDirsStr = "" irriType = 1 if len(irriDirsStr) < len(irri2DirsStr) else 2 cropType = 1 if len(cropDirsStr) < len(crop2DirsStr) else 2 result = smsTrans[langCode]["sms%s" % (templateType)] result = result.replace( "{1}", irriDirsStr if irriType == 1 else irri2DirsStr) result = result.replace( "{2}", cropDirsStr if cropType == 1 else crop2DirsStr) # return [templateType, irriType, cropType, result] return result def zoneToStr(zoneNo): if zoneNo == 1: return "NW" if zoneNo == 2: return "N" if zoneNo == 3: return "NE" if zoneNo == 4: return "W" if zoneNo == 5: return "C" if zoneNo == 6: return "E" if zoneNo == 7: return "SW" if zoneNo == 8: return "S" if zoneNo == 9: return "SE" # imgPath = "report_images/demoPics/field_img12_rvi_20221215.png" #"sampleField.png" # redZones = getRedZones(imgPath, False) # print(redZones) # 299-E61919, 60-3D1362, 17-11A75E, 247-F78959, 145-93102D # 186, 227, 131-B9E382,12182402,12247939; # create field dirc img def createFieldDirImg(imgPath, savePath="report_images/demoPics/dirImg.png", translations=None, langCode=None): img = Image.open(imgPath) imgWidth = img.size[0] imgHeight = img.size[1] # make single color img pixelMap = img.load() for x in range(imgWidth): for y in range(imgHeight): if 0 == pixelMap[x, y][3]: pixelMap[x, y] = (255, 255, 255, 0) else: pixelMap[x, y] = (255, 240, 179, 255) # resize if needed smallerLen = min(imgWidth, imgHeight) factor = 1 if smallerLen < 300: factor = round(300/smallerLen, 1) if factor % 0.2 == 0.1: factor += 0.1 if factor != 1: img = img.resize((round(imgWidth*factor), round(imgHeight*factor))) imgHeight = img.size[1] imgWidth = img.size[0] # add zone lines editableImg = ImageDraw.Draw(img) width1 = round(imgWidth/3) width2 = round(imgWidth*2/3) height1 = round(imgHeight/3) height2 = round(imgHeight*2/3) editableImg.line([(width1, 0), (width1, imgHeight)], (0, 0, 0), 2) editableImg.line([(width2, 0), (width2, imgHeight)], (0, 0, 0), 2) editableImg.line([(0, height1), (imgWidth, height1)], (0, 0, 0), 2) editableImg.line([(0, height2), (imgWidth, height2)], (0, 0, 0), 2) # add direction text def getLocalName(zone): # langCode is only expected as hi return getLocalDirByZone(zone, translations, langCode) if translations != None: zoneNames = list(map(getLocalName, zones)) else: zoneNames = list(map(zoneToStr, zones)) fontSize = 26 if translations != None else 50 if langCode == "bn": fontPath = "report_fonts/Hind_Siliguri/HindSiliguri-Light.ttf" elif langCode == "te": fontPath = "report_fonts/Noto_Sans_Telugu/static/NotoSansTelugu/NotoSansTelugu-Light.ttf" elif langCode == "ta": fontPath = "report_fonts/Noto_Sans_Tamil/static/NotoSansTamil/NotoSansTamil-Light.ttf" elif langCode == "kn": fontPath = "report_fonts/Noto_Sans_Kannada/static/NotoSansKannada/NotoSansKannada-Light.ttf" # elif langCode=="hi": # fontPath = "report_fonts/Mukta/Mukta-Light.ttf" else: fontPath = "report_fonts/Noto_Sans/NotoSans-Bold.ttf" font = ImageFont.truetype(fontPath, fontSize) widthF = round((width1-80)/2) heightF = round((height1-100)/2) # print(zoneNames) for zone in zones: dirOffset = getDirOffsetByZone( zone, width1, width2, widthF, height1, height2, heightF) index = zone-1 zoneName = zoneNames[index] editableImg.text(dirOffset, zoneName, (255, 50, 36), font, layout_engine=ImageFont.LAYOUT_RAQM) # editableImg.text((widthF, heightF), "NW", (255, 50, 36), font) # editableImg.text((width1+widthF, heightF), "N", (255, 50, 36), font) # editableImg.text((width2+widthF, heightF), "NE", (255, 50, 36), font) # editableImg.text((widthF, height1+heightF), "W", (255, 50, 36), font) # editableImg.text((width1+widthF, height1+heightF), # "C", (255, 50, 36), font) # editableImg.text((width2+widthF, height1+heightF), # "E", (255, 50, 36), font) # editableImg.text((widthF, height2+heightF), "SW", (255, 50, 36), font) # editableImg.text((width1+widthF, height2+heightF), # "S", (255, 50, 36), font) # editableImg.text((width2+widthF, height2+heightF), # "SE", (255, 50, 36), font) # save the result img.save(savePath) def getDirOffsetByZone(zone, width1, width2, widthF, height1, height2, heightF): if zone == 1: return (widthF, heightF) if zone == 2: return (width1+widthF, heightF) if zone == 3: return (width2+widthF, heightF) if zone == 4: return (widthF, height1+heightF) if zone == 5: return (width1+widthF, height1+heightF) if zone == 6: return (width2+widthF, height1+heightF) if zone == 7: return (widthF, height2+heightF) if zone == 8: return (width1+widthF, height2+heightF) if zone == 9: return (width2+widthF, height2+heightF) # tranlations = { # "hi": { # "north": "उत्तर", # 5 # "east": "पूर्व", # 5 # "west": "पश्चिम", # 6 # "south": "दक्षिण", # 6 # "ceNter": "केंद्र" # 6 # }, # "en": { # "north": "north", # 5 # "east": "east", # 5 # "west": "west", # 6 # "south": "south", # 6 # "ceNter": "center" # 6 # } # } # # for key, value in tranlations.items(): # # print(key, len(value)) # # exit() # lang = "en" # template = "0_" # irriRedZones = [1, 2, 3, 4, 5, 6, 7, 8, 9] # cropRedZones = [1, 2, 3, 4, 5] # smsTxt = getSmsFullInstructionsByZones( # template, irriRedZones, cropRedZones, tranlations[lang], lang) # print(smsTxt) # saveTxtToFile(smsTxt[3]) # print(len(smsTxt[3])) # imgToMask = "report_images/demoPics/field_img12_rvi_20221215.png" # createFieldDirImg(imgToMask, "report_images/demoPics/dirImg12_rvi_20221215.png") # createFieldDirImg(imgToMask, "report_images/demoPics/dirImghi.png", tranlations, "hi") # print("done") # cloudyPixels = [(255, 255, 0, 255), (0, 0, 0, 255), (255, 255, 255, 255)] def isCloudyImg(etciPath, ndviPath): img = Image.open(etciPath) pixelValues = list(img.getdata()) img2 = Image.open(ndviPath) pixelValues2 = list(img2.getdata()) colors = img.getcolors() print("colors: ", colors) print("totalPixels: ", len(pixelValues)) # print("pixels", pixelValues) # return 0 imgSize = img.size # w-h print("imgSize: ", imgSize) imgWidth = imgSize[0] imgHeight = imgSize[1] fieldPixels = 0 cloudyPixelsNo = 0 for x in range(imgWidth): for y in range(imgHeight): index = y*imgWidth+x rgba = pixelValues[index] if rgba[3] == 255: fieldPixels += 1 rgbaN = pixelValues2[index] isNRed = checkIfColorInList(rgbaN, redPixels) if isNRed: isYellow = rgba[0] >= 240 and rgba[1] >= 230 isBlack = rgba[2] == 0 and rgba[0] + \ rgba[1] < 100 and abs(rgba[0]-rgba[1]) < 50 if isYellow or isBlack: cloudyPixelsNo += 1 print("cloudyPixels", cloudyPixelsNo) print("fieldPixels", fieldPixels) return cloudyPixelsNo > fieldPixels*0.3 def analyseColors(etciPath, ndviPath): # analysing for cloudy imgETCI = Image.open(etciPath) imgNDVI = Image.open(ndviPath) pixelValues = list(imgETCI.getdata()) pixelValuesN = list(imgNDVI.getdata()) colorsETCI = imgETCI.getcolors() # colorsNDVI = imgNDVI.getcolors() c_255_255 = 0 c_245_245 = 0 c_240_240 = 0 whites = 0 blacks = 0 fieldPixels = 0 totalPixels = 0 fieldList = [] for color in colorsETCI: count = color[0] rgba = color[1] totalPixels += count if rgba[3] != 0: fieldList.append(color) fieldPixels += count if rgba[0] == 255 and rgba[1] == 255: c_255_255 += count if rgba[2] == 255: whites += count elif rgba[0] >= 245 and rgba[1] >= 245: c_245_245 += count elif rgba[0] >= 240 and rgba[1] >= 240: c_240_240 += count elif rgba[0] == 0 and rgba[1] == 0 and rgba[2] == 0: blacks += count def cloudySort(color): rgba = color[1] return rgba[0]*4+rgba[1]*2+rgba[2] def countSort(color): return color[0] fieldList.sort(key=cloudySort) fieldList2 = fieldList.copy() fieldList2.sort(key=countSort, reverse=True) imgSize = imgETCI.size # w-h print("imgSize: ", imgSize) imgWidth = imgSize[0] imgHeight = imgSize[1] cloudies = 0 for x in range(imgWidth): for y in range(imgHeight): index = y*imgWidth+x rgba = pixelValues[index] if rgba[3] == 255: rgbaN = pixelValuesN[index] isNRed = checkIfColorInList(rgbaN, redPixels) if isNRed: isYellow = rgba[0] >= 240 and rgba[1] >= 230 isBlack = rgba[2] == 0 and rgba[0] + \ rgba[1] < 100 and abs(rgba[0]-rgba[1]) < 50 if isYellow or isBlack: cloudies += 1 # , fieldList, fieldList2[:min(20, len(fieldList2))]) return (totalPixels, fieldPixels, whites, blacks, c_255_255, c_245_245, c_240_240, cloudies) # ndviPath = "report_images/demoPics/field_img14_20220825_ndvi.png" # etciPath = "report_images/demoPics/field_img11_etci_20221212.png" # etciPath = "report_images/demoPics/field_img14_20220825_etci.png" # print(isCloudyImg(etciPath, ndviPath)) # print(analyseColors(etciPath, ndviPath)) def createDirOnMapImg(dirImgPath, mapImgPath, mapDimens, outPath): dirImg = Image.open(dirImgPath, "r") mapImg = Image.open(mapImgPath) logoImg = Image.open( "report_images/farmonaut_logo_white.png", "r") # 390x113 horiPadPer = mapDimens[2] verPadPer = mapDimens[3] horiPad = (mapDimens[0]*horiPadPer)/(1+2*horiPadPer) # in pixels verPad = (mapDimens[1]*verPadPer)/(1+2*verPadPer) # in pixels width = mapDimens[0]/(1+2*horiPadPer) # height = mapDimens[1]/(1+2*verPadPer) magnifyRatio = dirImg.size[0]/width finalVerPad = magnifyRatio*verPad logoHeight = finalVerPad*0.7 print(magnifyRatio, logoHeight, horiPad, verPad) resizedLogoImg = logoImg.resize( (round(logoHeight*390/113), round(logoHeight)), Image.LANCZOS) # resizedDirImg.save("report_images/demoPics/resizeDirImg.png") # , round(horiPad+width), round(verPad+height) resizedMapImg = mapImg.resize((round( magnifyRatio*mapImg.size[0]), round(magnifyRatio*mapImg.size[1])), Image.LANCZOS) resizedMapImg.paste(dirImg, (round(magnifyRatio*horiPad), round(finalVerPad)), mask=dirImg) resizedMapImg.paste(resizedLogoImg, mask=resizedLogoImg) resizedMapImg.save(outPath) # dirImgPath="report_images/demoPics/dirImghi.png" # mapImgPath="report_images/demoPics/fieldMap.jpg" # dimens=(167.91809200993967, 335.83618401987934, # 0.5328091778105274, 0.09999999999999996) # 44.64947605769587 153.71462885606567 # aspect: 0.2904699207224087 # new w-h: 76.85731442803284 153.71462885606567 # len 184.4575546272788 # 167.91809200993967 335.83618401987934 18 0.5492486021588433 0.1 0.1 # 92.2287758, 184.46091 (37.845, 15.373)/ # (167.91809200993967, 335.83618401987934, 0.5328091778105274, 0.09999999999999996) # outPath="report_images/demoPics/dirMapImg.png" # createDirOnMapImg(dirImgPath, mapImgPath, dimens, outPath)